使用某些道具触发useeffect()react钩子,但在效果中引用了其他人



i有一个react用例,其中组件获取 category prop向下传递,并且还具有局部状态值 limit。我所需的行为是:

  1. 当选择category时,limit如果当前高于10。

  2. category清除时,limit如果当前低于50,则将重置为50。

  3. category不变时,用户可以选择他们想要的任何limit

我将这种行为设置为效应钩子(底部更完整的示例(:

  useEffect(() => {
    if (category && limit > 10) {
      setLimit(10);
    } else if (!category && limit < 50) {
      setLimit(50);
    }
  }, [category]);

这可以正常工作,但是与React-Hooks/详尽的Linter规则相比,该规则说我应该在依赖关系数组中加入limit。我实际上不想要那个,因为我不希望limit的用户启动更改触发任何内容,并且我希望用户能够超过高/低阈值,而该类别没有更改。<<<<<<<<<<

是否有一种"正确"的方法来完成此操作,以尊重钩子的规则,同时又不引入更多钩子以兼顾所有不同的可能的道具/状态过渡?

更完整的示例:

function App() {
  const [category, setCategory] = useState(true);
  return (
    <div className="App">
      <button onClick={() => setCategory(!category)}>Toggle category</button>
      <List category={category} />
    </div>
  );
}
function List({ category }) {
  const [limit, setLimit] = useState(10);
  // Change the limit when category changes
  // BUT only if limit is above/below a threshold!
  useEffect(() => {
    if (category && limit > 10) {
      setLimit(10);
    } else if (!category && limit < 50) {
      setLimit(50);
    }
  }, [category]);
  return (
    <div>
      <select value={limit} onChange={e => setLimit(e.target.value)}>
        {[5, 10, 25, 50, 100].map(d => (
          <option key={d}>{d}</option>
        ))}
      </select>
      <div>Category is {category ? "on" : "off"}</div>
      <div>Would show {limit} items</div>
    </div>
  );
}

使用ref跟踪上一个类别怎么样?它们是函数组件中实例变量的等效词。

这样,您可以进行比较,并且仅在类别更改时才能运行效应代码。

const prevCat = useRef(null);
useEffect(() => {
  if (prevCat.current === category) return;
  prevCat.current = category;
  ...
}, [category, limit]);

切换到useReducer()category一起管理limit,并将两者都传递给组件而不是使用本地状态。这样,只有当一个值更改(或两者(时,组件才会渲染。

const initialState = {
    category: true,
    limit: 10
};
function reducer(state, action) {
    switch (action.type) {
    case 'toggleCategory':
        if (state.category) {
            return {category: false, limit: Math.min(50, state.limit)};
        }
        else {
            return {category: true, limit: Math.max(10, state.limit)};
        }
    case 'setLimit':
        return {...state, limit: action.limit};
    default:
        throw new Error();
    }
}
function App() {
    const [state, dispatch] = useReducer(reducer, initialState);
    // React guarantees that dispatch function identity is stable and won’t change on re-renders.
    // This is why it’s safe to omit from the useEffect or useCallback dependency list.
    const toggleCategory = useCallback(() => dispatch({type: 'toggleCategory'}));
    const setLimit = useCallback(limit => dispatch({type: 'setLimit', limit}));
    return (
        <div className="App">
            <button onClick={toggleCategory}>Toggle category</button>
            <List {...state} setLimit={setLimit} />
        </div>
    );
}
function List({ category, limit, setLimit }) {
    return (
        <div>
            <select value={limit} onChange={e => setLimit(e.target.value)}>
                {[5, 10, 25, 50, 100].map(d => (
                    <option key={d}>{d}</option>
                ))}
            </select>
            <div>Category is {category ? "on" : "off"}</div>
            <div>Would show {limit} items</div>
        </div>
    );
}

您可以使用setter的功能版本:

useEffect(() => {
    setLimit((currentLimit) => {
        if (category && currentLimit > 10) {
          return 10;
        } else if (!category && currentLimit < 50) {
          return 50;
        } else return currentLimit;
    });
  }, [category]);

相关内容

  • 没有找到相关文章