使用useContext(Reactjs)时如何停止重新渲染?

时间:2020-06-15 05:46:14

标签: reactjs react-hooks use-reducer use-context

我有一个简单的react应用,其中有一个 FruitsList 组件用于显示列表中的水果,一个 FruitForm 组件用于添加水果,并且都包含在水果组件中。我正在使用useContextuseReducer来管理水果的状态。我已经为它创建了一个 FruitContext 。我想停止 FruitForm 的重新渲染,因为它仅使用分派功能,并且每次添加新水果时都无法重新渲染它。请提出任何解决方案。

表单组件

const Form = () => {

  const { dispatch } = useContext(FruitsContext);
  const { setLoading } = useContext(LoaderContext);

  let formRef = null;
  const fruit = {};

  const formSubmitHandler = async (event) => {
      event.preventDefault();
      setLoading(true);
      await fetch('https://fruit-basket-74269.firebaseio.com/fruits.json', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify(fruit)
        });
        dispatch({type: 'ADD', fruit: fruit});
        // formRef.reset();
        setLoading(false);
      }

      return (
        <Card>
          {console.log('[Form]')}
          <form ref={ref => formRef = ref} onSubmit={formSubmitHandler} className={style.form} autoComplete="off">
            <div className={style.formGroup}>
              <input onChange={event => fruit.item = event.target.value} className={style.input} type="text" id="name" placeholder="Enter fruit name" />
              <label className={style.label} htmlFor="name">Name</label>
            </div>
            <div className={style.formGroup}>
              <input onChange={event => fruit.qty = event.target.value} className={style.input} type="number" min="0" id="qty" placeholder="Enter quantity" />
              <label className={style.label} htmlFor="qty">Quantity</label>
            </div>
            <Button>Add Fruit</Button>
          </form>
        </Card>
      )
}

export default React.memo(Form);

水果列表

const FruitList = () => {

  const { fruits } = useContext(FruitsContext);
  console.log('[FruitList]:', fruits);

  return useMemo(() => {

    return (
      <div className={style.fruitList}>
        <h2 className={style.heading}>Fruits</h2>
        <hr />
        <div className={style.list}>
          <FruitCard name={'Apple'} qty={15} />
          <FruitCard name={'Orange'} qty={10} />
          <FruitCard name={'Grapes'} qty={20} />
        </div>
      </div>
    );
  }, []);
}

export default FruitList;

水果

const Fruits = () => {

  console.log('[Fruits Parent]');

  // const { loading } = useContext(LoaderContext);

  return (
    <div className={style.fruits}>
      {/* {loading && <Loader />} */}
      <Form />
      <br />
      <Filter />
      <br />
      <FruitList />
    </div>
  )
}

export default Fruits

FruitContext

export const FruitsContext = createContext();

const FruitsProvider = ({children}) => {
  const [fruits, dispatch] = useReducer(reducer, []);

  const value = ({
    fruits, dispatch
  });

  return (
    <FruitsContext.Provider value={value}>
      { children }
    </FruitsContext.Provider>
  );
}

export default FruitsProvider;

FruitReducer

export default (state, action) => {
  switch(action.type) {
    case 'LOAD':
      return action.fruits
    case 'ADD':
      console.log('[Pre-Action]', state);
      const newList = [...state];
      newList.push(action.fruit);
      console.log('[Post-Action]', newList);
      return newList;
    case 'DELETE':
      return state.filter(fruit => fruit.id !== action.id);
    default: return state;
  }
}

1 个答案:

答案 0 :(得分:0)

如果提供者值中的任何值发生更改,则消耗上下文的组件将始终重新呈现。不管您是否实际使用该值(例如,在这种情况下,即使您仅拉出调度功能)。

通常,对于大多数React应用程序,您不需要优化类似的内容,React已经非常快,并且不会损害其他一些渲染器。任何性能问题都可以在何时何地解决。 如果要从一开始就进行优化,则可以将reducers状态拆分并分配到两个不同的上下文中。它们都可以放在同一个ProviderComponent中,但是必须有两个不同的Context.Provider组件。一个将状态用作值,另一个将调度功能用作值。 如果您随后使用了调度上下文,那么如果调度操作更改了状态,则不会导致组件重新呈现。

//更新

例如:

const FruitsProvider = ({children}) => {
  const [fruits, dispatch] = useReducer(reducer, []);

  return (
    <FruitsStateContext.Provider value={fruits}>
      <FruitsDispatchContext.Provider value={dispatch}>
        { children }
      </FruitsDispatchContext.Provider>
    </FruitsStateContext.Provider>
  );
}

我还建议不要直接导出上下文,而是导出暴露状态或调度信息的钩子。

例如

export const useFruits = () => {
  const fruitsState = React.useContext(FruitsStateContext);
  if (!fruitsState) {
    throw new Error('you cant use the useFruits hook outside the FruitsStateContext');
  }
  return fruitsState;
}