i有一个react用例,其中组件获取 category
prop向下传递,并且还具有局部状态值 limit
。我所需的行为是:
-
当选择
category
时,limit
如果当前高于10。 -
当
category
清除时,limit
如果当前低于50,则将重置为50。 -
当
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]);