我有一个React用例,其中一个组件获得了一个category
属性的传递,并且还具有一个局部状态值limit
。我想要的行为是:
选择category
后,如果limit
当前高于10,则会重置为10。
清除category
时,如果limit
当前低于50,则会重置为50。
虽然category
不变,但是用户可以选择他们想要的任何limit
。
我在效果挂钩中设置了此行为,如下所示(底部更完整的示例):
useEffect(() => {
if (category && limit > 10) {
setLimit(10);
} else if (!category && limit < 50) {
setLimit(50);
}
}, [category]);
这很好用,但是与react-hooks/exhaustive-deps linter rule冲突,后者表明我应该在依赖项数组中包含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>
);
}
答案 0 :(得分:1)
如何使用ref
来跟踪上一个类别?它们相当于功能组件中的instance variables。
这样,您可以进行比较,并且仅在类别发生更改时才运行效果代码。
const prevCat = useRef(null);
useEffect(() => {
if (prevCat.current === category) return;
prevCat.current = category;
...
}, [category, limit]);
答案 1 :(得分:0)
切换到useReducer()
来与limit
一起管理category
,并将两者都传递给您的组件,而不是使用本地状态。这样,仅当一个值更改(或两个值都更改)时才呈现组件。
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>
);
}
答案 2 :(得分:0)
您可以使用setter的功能版本:
useEffect(() => {
setLimit((currentLimit) => {
if (category && currentLimit > 10) {
return 10;
} else if (!category && currentLimit < 50) {
return 50;
} else return currentLimit;
});
}, [category]);