useState против useReducer - PullRequest
       6

useState против useReducer

0 голосов
/ 12 февраля 2019

useReducer обычно предпочтительнее useState, когда у вас есть сложная логика состояния, которая включает в себя несколько значений, или когда следующее состояние зависит от предыдущего. useReducer также позволяет оптимизировать производительность компонентов, запускающих глубокие обновления, поскольку вы можете передавать диспетчер вместо обратного вызова.

(цитата из https://reactjs.org/docs/hooks-reference.html#usereducer)

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

Я пробовал оба варианта, но они не отличаются.

Я сравнил оба подхода следующим образом:

const [state, updateState] = useState();
const [reducerState, dispatch] = useReducer(myReducerFunction);

Я передал каждый из них один раз объекту контекста, который использовался в более глубоком дочернем элементе (я просто побежал отдельнотесты, заменяя значение на функцию, которую я хотел проверить).

<ContextObject.Provider value={updateState // dispatch}>

Дочерние элементы содержали эти функции

const updateFunction = useContext(ContextObject);
useEffect(
  () => {
    console.log('effect triggered');
    console.log(updateFunction);
  },
  [updateFunction]
);

В обоих случаях, когдародительский перерисован (из-за другого локального изменения состояния), эффект никогда не запускался, указывая на то, что функция обновления не меняется между визуализациями. Я неправильно прочитал жирное предложение в кавычке?rlooking?

Ответы [ 2 ]

0 голосов
/ 22 июня 2019

Когда вам нужно позаботиться об этом

Если вы создадите обратный вызов при рендеринге и передадите его дочернему компоненту, реквизиты этого дочернего элемента будут изменены.Тем не менее, обычный компонент будет перерисовываться (в виртуальный дом), когда родительский рендеринг, даже реквизиты остаются прежнимиИсключением является classComponent, который реализует shouldComponentUpdate и сравнивает реквизиты (такие как PureComponent).

Это оптимизация, и вы должны заботиться о ней только в том случае, если перерисовка дочернего компонента требует значительных вычислений (если вы визуализируете его на один и тот же экран несколько раз или если он потребует глубокого или значительного повторного воспроизведения).

В этом случае вы должны убедиться, что: 1. Ваш ребенок является компонентом класса, который расширяет PureComponent 2. Избегайте передачи вновь созданной функции в качестве реквизита.Вместо того, чтобы передать диспетчер, установщик возвратил из React.useState или запомненного настроенного установщика.

Использование запомненного настроенного установщика

Хотя я бы не рекомендовал создавать уникальный запомненный установщик для определенного компонента (тамэто несколько вещей, на которые нужно обратить внимание), вы можете использовать общий хук, который позаботится о реализации для вас.

Вот пример хука useObjState, который предоставляет простой API и который не вызовет дополнительных повторных визуализаций.


const useObjState = initialObj => {
  const [obj, setObj] = React.useState(initialObj);
  const memoizedSetObj = React.useMemo(() => {
    const helper = {};
    Object.keys(initialObj).forEach(key => {
      helper[key] = newVal =>
        setObj(prevObj => ({ ...prevObj, [key]: newVal }));
    });
    return helper;
  }, []);
  return [obj, memoizedSetObj];
};

function App() {
  const [user, memoizedSetUser] = useObjState({
    id: 1,
    name: "ed",
    age: null,
  });

  return (
      <NameComp
        setter={memoizedSetUser.name}
        name={user.name}
      />
  );
}

const NameComp = ({name, setter}) => (
  <div>
    <h1>{name}</h1>
      <input
        value={name}
        onChange={e => setter(e.target.value)}
      />
  </div>
)

Демо

0 голосов
/ 12 февраля 2019

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

Приведенный выше оператор не пытается указать, что установщик возвратилuseState создается заново при каждом обновлении или рендере.Это означает, что когда у вас есть сложная логика для обновления состояния, вы просто не будете использовать установщик напрямую для обновления состояния, вместо этого вы напишите сложную функцию, которая в свою очередь вызовет установщик с обновленным состоянием, например,

const handleStateChange = () => {
   // lots of logic to derive updated state
   updateState(newState);
}

ContextObject.Provider value={{state, handleStateChange}}>

Теперь в вышеприведенном случае каждый раз при повторной визуализации родителя создается новый экземпляр handleStateChange, в результате чего получатель контекста также выполняет повторную визуализацию.

Решением вышеуказанного случая является использование useCallback и запомните метод обновления состояния и используйте его.Однако для этого вам необходимо позаботиться о проблемах закрытия, связанных с использованием значений в методе.

Следовательно, рекомендуется использовать useReducer, который возвращает метод dispatch, который не меняется междуи вы можете иметь логику манипуляции в редукторах.

...