'value' из реквизита и thisValue в состоянии, antipattern? - PullRequest
2 голосов
/ 01 ноября 2019

В последнее время я пишу много кода, который выглядит следующим образом:

const SomeComponent: React.FunctionComponent<SomeComponentProps> = props => {
  const { value } = props;
  const [thisValue, setThisValue] = React.useState(value);

  const handleChange = React.useCallback(e => {
    setThisValue(e.target.value);
  }, []);
 ....

Полный пример на CodePen.

Где я нахожусьпередача значения инициализации из реквизита, а затем изменение этого значения внутри компонента.

Он делает то, что я хочу, но я начинаю подозревать это.

Это антипаттерн?

Ответы [ 2 ]

3 голосов
/ 01 ноября 2019

Вот хорошая (немного старая) статья, в которой обсуждается состояние инициализации с помощью реквизита:

https://medium.com/@justintulk/react-anti-patterns-props-in-initial-state-28687846cc2e

Документы React называют это анти-паттерном:

«Использование реквизитов для генерации состояния в getInitialState часто приводит к дублированию« источника правды », то есть того, где находятся реальные данные. Это потому, что getInitialState вызывается только при первом создании компонента.»

Из документов, есть исключение:

"Используйте этот шаблон только в том случае, если вы намеренно хотите игнорировать обновления проп. В этом случае имеет смысл переименовать проп, чтобы он назывался initialColor или defaultColor. Вы можетезатем вынудите компонент «сбросить» свое внутреннее состояние, изменив ключ, когда это необходимо. "

При этом я также время от времени обнаруживаю, что делаю это.

Примером, где я нахожу это полезным, являются компоненты, которые позволяют пользователям редактировать сложное состояние. Вы можете инициализировать состояние с помощью реквизита, и компонент внутренне обрабатывает изменения в этом состоянии. Если вы хотите сбросить состояние при смене реквизита, у вас есть два варианта:

Первый - прослушать изменения и вызвать setState, например:

componentDidUpdate(prevProps, prevState) {
  if (prevProps.inputValue !== this.props.inputValue) {
    this.setState({ inputVal: this.props.inputValue })
  }
}

Рекомендуемый вариант - датькомпонент ключ, который зависит от реквизита, который должен вызвать сброс. Затем при изменении реквизита состояние компонента будет повторно инициализировано:

<MyComponent initProp={someValue} key={`key_${someValue}`/>

Я бы порекомендовал эти опции только для сложного состояния. В большинстве примеров родительский компонент может содержать это состояние, а внутренний компонент может использовать обратный вызов для обновления родительского элемента изменениями.

0 голосов
/ 01 ноября 2019

Вот пример анти-паттерна, где вы не позволяете реквизитам вести и только устанавливаете состояние на монтировании или конструкторе. Я пытаюсь сделать компонент формы с состоянием, но будет сигнализировать потребителю (родителю), когда пользователь отправит его. Потребитель имеет возможность сбросить форму:

function App() {
  const [initial, setInitial] = React.useState({
    name: 'init',
  });
  const submit = React.useCallback(form => {
    console.log('form is:',form);
  }, []);
  return (
    <div>
      <button onClick={() => setInitial({ name: 'reset' })}>
        reset
      </button>
      <Form initial={initial} submit={submit} />
    </div>
  );
}

function Form({ initial, submit }) {
  const [form, setForm] = React.useState(initial);
  const onChange = React.useCallback(e => {
    const name = e.target.value;
    setForm(form => ({ ...form, name }));
  }, []);
  const onSubmit = React.useCallback(
    e => {
      e.preventDefault();
      setForm(form => {
        submit(form);
        return form;
      });
    },
    [submit]
  );
  //witout this effect reset will not work
  React.useEffect(() => setForm(initial), [initial]);
  return (
    <form onSubmit={onSubmit}>
      <input
        type="text"
        value={form.name}
        onChange={onChange}
      />
      <button type="submit">Save</button>
    </form>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...