0

我有一个简单的反应应用程序,其中有一个FruitsList组件用于显示列表中的水果,一个FruitForm组件用于添加水果,两者都包含在Fruits组件中。我正在使用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;

水果减速机

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;
  }
}
4

1 回答 1

0

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

通常,对于大多数 React 应用程序,您不需要优化类似的东西,React 已经非常快,并且一些额外的重新渲染不会造成伤害。任何性能问题都可以在发生的时间和地点得到解决。如果您想从一开始就进行优化,您可以拆分您的减速器状态并分派到两个不同的上下文中。它们都可以放入同一个 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;
}
于 2020-06-15T16:04:31.103 回答