Реакция включения и связи с компонентом контейнера - PullRequest
0 голосов
/ 22 марта 2019

Я ищу подходящий шаблон / способ разработки этого с помощью React, но пока не нашел ничего подходящего / элегантного.

Я в основном хочу написать движок форм.Для каждого ввода в форме мне нужны некоторые общие поведения.

Я прочитал в документации React, что наследование не является правильным способом;Хороший способ - разработать общий компонент и специализировать его по составу.

В моем верхнем компоненте я хочу написать что-то вроде:

<Form>
    <TextInput .../>
    <TextInput .../>
    <EmailInput .../>
</Form>

Каждый тип ввода должен в основном всегда делать одно и то же: пример: проверять это значение на соответствие его валидаторам и т. Д.

Итак, я разработал общий компонент FormInput, который содержит все эти стандартные поведения.Когда я пишу свой компонент TextInput, он выглядит следующим образом:

export default class TextInput extends React.Component {

    constructor(props) {
        super(props);
    }

    render() {
        return (
            <FormInput>
                <input name={this.props.name} 
                       type='text'
                       onChange={this.onChange}
                       onBlur={this.dirty}
                       value={this.state.value}
                />
            </FormInput>
        );
    }

}

Теперь проблема в том, что this.onChange или this.dirty - это стандартные поведения, расположенные в компоненте FormInput,очевидно, я не могу получить к ним прямой доступ ...

Как правильно подключить включенный контент к его компоненту контейнера?

РЕДАКТИРОВАТЬ

Просто чтобы прояснить и подвести итог цели, я в основном хочу сделать общий компонент и включенный контент / шаблон.Проблема заключается в том, что мне нужно привязать конкретный шаблон DOM (который находится в конкретном компоненте) к универсальным обработчикам (которые находятся в универсальном компоненте).

Спасибо заранее!

Ответы [ 3 ]

0 голосов
/ 22 марта 2019

Согласно вашему комментарию:

FormInput должен содержать все стандартные вещи, такие как изменения и проверки. TextInput должен содержать только тег html <input> и его стиль.

Кажется, что ваша иерархия неверна, на мой взгляд.
Я бы создал Input, который отвечает за то, как должен выглядеть примитив input, и Form, который отвечает за то, как должны вести себя вложенные input и какие данные они содержат.

Вот пример, очевидно, вы можете стилизовать его другими способами, а не встроенными стилями.

const Input = ({ label, ...rest }) => (
  <div style={{ display: "flex" }}>
    <div style={{ display: "flex", alignItems: "center", margin: "10px 0" }}>
      <label style={{ minWidth: "100px", margin: "0 10px" }}>{label}</label>
      <input style={{ padding: "0.5em" }} {...rest} />
    </div>
  </div>
);

class Form extends React.Component {
  state = {
    fName: "",
    lName: "",
    age: null
  };

  onChange = e => {
    const { name, value } = e.target;
    this.setState({ [name]: value });
  };

  onSubmit = e => {
    e.preventDefault();
    const { onSubmit } = this.props;
    onSubmit(this.state);
  };

  render() {
    const { fName, lName, age } = this.state;
    return (
      <form onSubmit={this.onSubmit}>
        <Input
          type="text"
          name="fName"
          label="First Name"
          value={fName}
          required
          onChange={this.onChange}
        />
        <Input
          type="text"
          name="lName"
          label="Last Name"
          value={lName}
          required
          onChange={this.onChange}
        />
        <Input
          type="number"
          name="age"
          label="Age"
          value={age}
          required
          onChange={this.onChange}
        />
        <button type="submit">Submit</button>
      </form>
    );
  }
}

function App() {
  return (
    <div>
      <Form onSubmit={data => console.log(data)} />
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"/>
0 голосов
/ 22 марта 2019

Я наконец-то понял это!

Вот мое решение:

export default class TextInput extends React.Component {

    constructor(props) {
        super(props);
    }

    template(state, formInput) {
        return (
             <input name={this.props.name} 
                    type='text'
                    onChange={formInput.onChange}
                    onBlur={formInput.dirty}
                    value={state.value}/>
        );
    }

    render() {
        return (
            <FormInput {...this.props} template={this.template} />
        );
    }

}

Это было наконец легче, чем ожидалось, но я не использовал transclusion.

Компонент FormInput содержит все, от состояния до общего кода / поведения (например, dirty или onChange).

Компонент TextInput только создает экземпляр FormInput и содержитфункция с именем template, которая принимает удаленное состояние и сам общий компонент FormInput для доступа к своим функциям.

Затем в FormInput я получил следующее:

render() {
    return this.props.template(this.state, this);
}

Общий компонент ввода вызывает функцию template и передает то, что необходимо для рендеринга.

Таким образом, я отсоединяю представление от его поведения.

Надеюсь, вам понравится и что это кому-нибудь поможет позже;)

0 голосов
/ 22 марта 2019

Если я правильно понял, ваши onChange и dirty методы реализованы внутри компонента FormInput.В этом случае элемент input должен быть размещен внутри метода рендеринга компонента и не передаваться как дочерний элемент.

Этот метод рендеринга FormInput должен выглядеть примерно так:

render(){
    return  <input name={this.props.name} 
                       type={this.props.type}
                       onChange={this.onChange}
                       onBlur={this.dirty}
                       value={this.state.value}
                />
}

Кроме того, FormInput должен иметь свойства name и type, чтобы использовать его следующим образом:

 <FormInput name='A name' type='text' />

Полагаю, изначально вы пытались иметь возможность передать также вход, заключенный в другие элементы html.Если это так, с моим предложением вы можете просто обернуть новый FormInput, чтобы получить тот же результат.

ОБНОВЛЕНИЕ:

Чтобы отобразить другие типы элементов формы внутри компонента FormInput, и есливы не чувствуете себя комфортно с условным рендерингом, вы можете попробовать другой способ, используя createElement метод внутри рендеринга с типом yout prop.

Дополнительная информация здесь: Создать элемент

Я использовал его раньше, и очень хорошо работал над моими проектами.

...