React hooks 如何根据 props 变化更好地更新多个状态
React hooks how to better update multiple states based on props change
假设我有一个带有排序数据的 table,我想将它存储在 state(甚至 3 个独立的状态)上。假设这个状态可以被 child 改变。
有没有办法做到这一点 而无需 3 个不同的 useEffects,我想看看是否有可能通过 仅使用 1 次来实现与下面相同的效果效果?
import React, { useState, useEffect } from "react";
function Table({ initialSortDirection, initialSortParam, initialSortEnabled }) {
const [currentSortData, setSortData] = useState({
sortDirection: initialSortDirection,
sortParam: initialSortParam,
hasSort: initialSortEnabled
});
useEffect(() => {
setSortData({ ...currentSortData, sortDirection: initialSortDirection });
}, [initialSortDirection]);
useEffect(() => {
setSortData({ ...currentSortData, sortParam: initialSortParam });
}, [initialSortParam]);
useEffect(() => {
setSortData({ ...currentSortData, hasSort: initialSortEnabled });
}, [initialSortEnabled]);
return (<SomeComponent onChangeSort={setSortData} />)
}
按照老派的方式,我可能会使用 componentWillReceiveProps 并比较 nextProps 以查看它们是否发生了变化,但现在我很难找到一种简洁的方法来做到这一点 "at once" 并且仅在改变。
作为下图的可视化示例,您可以通过单击单元格或更改 "knobs" 来更改排序。
编辑 1
假设其他事情可能会影响状态,我不想用未更改的初始 prop 覆盖更新后的状态。我相应地更新了代码
编辑 2
添加了故事书图片
你可以有一个 useEffect()
来监听几个状态的变化:
useEffect(() => {
setSortData({
...currentSortData,
sortDirection: initialSortDirection,
sortParam: initialSortParam,
hasSort: initialSortEnabled
});
}, [initialSortDirection, initialSortParam, initialSortEnabled]);
这是您正在寻找的行为吗?
这是我只用一个 useEffect()
.
的方法
我会在 useRef
中保留 props
最后的值(来自之前的渲染)并检查每个 属性 的差异并决定是否应该更新state
。之后,我将 ref
值更新为当前 props
,以便在下一次渲染期间针对未来 props
进行检查,依此类推。
function App() {
const [initialState, setInitialState] = React.useState({
initialProp1: 'A',
initialProp2: 'A'
});
return(
<Table
{...initialState}
setInitialState={setInitialState}
/>
);
}
function Table({initialProp1, initialProp2, setInitialState}) {
const [myState, setMyState] = React.useState({
prop1: initialProp1,
prop2: initialProp2
});
const lastProps = React.useRef({
initialProp1,
initialProp2
});
React.useEffect(()=>{
if (lastProps.current.initialProp1 !== initialProp1) {
console.log('1 changed');
setMyState((prevState)=>{
return({
...prevState,
prop1: initialProp1
});
});
}
if (lastProps.current.initialProp2 !== initialProp2) {
console.log('2 changed');
setMyState((prevState)=>{
return({
...prevState,
prop2: initialProp2
});
});
}
lastProps.current = {
initialProp1,
initialProp2
}
});
function changeState() {
setMyState((prevState) => {
return({
...prevState,
prop2: 'B'
});
});
}
function changeProps() {
setInitialState({
initialProp1: 'A',
initialProp2: 'C'
});
}
return(
<React.Fragment>
<div>This is Table <b>props</b> initialProp1: {initialProp1}</div>
<div>This is Table <b>props</b> initialProp2: {initialProp2}</div>
<div>This is Table <b>state</b> prop1: {myState.prop1}</div>
<div>This is Table <b>state</b> prop2: {myState.prop2}</div>
<button onClick={changeState}>Change Table state</button>
<button onClick={changeProps}>Change props that comes from parent</button>
</React.Fragment>
);
}
ReactDOM.render(<App/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
我在 遇到类似问题 时遇到了这个问题,但对 cbdevelopers 对他使用 useRef
的回答不满意,因为我觉得你应该不需要这个。 reactflux 的一位友好人士指出了一个更优雅的解决方案:
const Table = (props) => {
const { initialSortDirection, initialSortParam, initialSortEnabled } = props;
const [currentSortData, setSortData] = useState({
sortDirection: initialSortDirection,
sortParam: initialSortParam,
hasSort: initialSortEnabled
});
useEffect(() => {
setSortData((prevSortData) => ({
...prevSortData,
sortDirection: initialSortDirection
}));
}, [initialSortDirection]);
useEffect((prevSortData) => {
setSortData(() => ({
...prevSortData,
sortParam: initialSortParam
});
}, [initialSortParam]);
useEffect(() => {
setSortData((prevSortData) => ({
...prevSortData,
hasSort: initialSortEnabled
}));
}, [initialSortEnabled]);
return (<SomeComponent onChangeSort={setSortData} />)
}
我知道您想将所有内容合并为一个,但我不推荐这样做。 您想分离关注点,在道具更新时始终触发正确的效果。
https://reactjs.org/docs/hooks-effect.html#tip-use-multiple-effects-to-separate-concerns
请注意,OP 的解决方案是假的,好像两个道具同时更新,使用多个效果时只会保留最后一个状态变化。
希望这对某人有所帮助。
假设我有一个带有排序数据的 table,我想将它存储在 state(甚至 3 个独立的状态)上。假设这个状态可以被 child 改变。 有没有办法做到这一点 而无需 3 个不同的 useEffects,我想看看是否有可能通过 仅使用 1 次来实现与下面相同的效果效果?
import React, { useState, useEffect } from "react";
function Table({ initialSortDirection, initialSortParam, initialSortEnabled }) {
const [currentSortData, setSortData] = useState({
sortDirection: initialSortDirection,
sortParam: initialSortParam,
hasSort: initialSortEnabled
});
useEffect(() => {
setSortData({ ...currentSortData, sortDirection: initialSortDirection });
}, [initialSortDirection]);
useEffect(() => {
setSortData({ ...currentSortData, sortParam: initialSortParam });
}, [initialSortParam]);
useEffect(() => {
setSortData({ ...currentSortData, hasSort: initialSortEnabled });
}, [initialSortEnabled]);
return (<SomeComponent onChangeSort={setSortData} />)
}
按照老派的方式,我可能会使用 componentWillReceiveProps 并比较 nextProps 以查看它们是否发生了变化,但现在我很难找到一种简洁的方法来做到这一点 "at once" 并且仅在改变。
作为下图的可视化示例,您可以通过单击单元格或更改 "knobs" 来更改排序。
假设其他事情可能会影响状态,我不想用未更改的初始 prop 覆盖更新后的状态。我相应地更新了代码
编辑 2 添加了故事书图片
你可以有一个 useEffect()
来监听几个状态的变化:
useEffect(() => {
setSortData({
...currentSortData,
sortDirection: initialSortDirection,
sortParam: initialSortParam,
hasSort: initialSortEnabled
});
}, [initialSortDirection, initialSortParam, initialSortEnabled]);
这是您正在寻找的行为吗?
这是我只用一个 useEffect()
.
我会在 useRef
中保留 props
最后的值(来自之前的渲染)并检查每个 属性 的差异并决定是否应该更新state
。之后,我将 ref
值更新为当前 props
,以便在下一次渲染期间针对未来 props
进行检查,依此类推。
function App() {
const [initialState, setInitialState] = React.useState({
initialProp1: 'A',
initialProp2: 'A'
});
return(
<Table
{...initialState}
setInitialState={setInitialState}
/>
);
}
function Table({initialProp1, initialProp2, setInitialState}) {
const [myState, setMyState] = React.useState({
prop1: initialProp1,
prop2: initialProp2
});
const lastProps = React.useRef({
initialProp1,
initialProp2
});
React.useEffect(()=>{
if (lastProps.current.initialProp1 !== initialProp1) {
console.log('1 changed');
setMyState((prevState)=>{
return({
...prevState,
prop1: initialProp1
});
});
}
if (lastProps.current.initialProp2 !== initialProp2) {
console.log('2 changed');
setMyState((prevState)=>{
return({
...prevState,
prop2: initialProp2
});
});
}
lastProps.current = {
initialProp1,
initialProp2
}
});
function changeState() {
setMyState((prevState) => {
return({
...prevState,
prop2: 'B'
});
});
}
function changeProps() {
setInitialState({
initialProp1: 'A',
initialProp2: 'C'
});
}
return(
<React.Fragment>
<div>This is Table <b>props</b> initialProp1: {initialProp1}</div>
<div>This is Table <b>props</b> initialProp2: {initialProp2}</div>
<div>This is Table <b>state</b> prop1: {myState.prop1}</div>
<div>This is Table <b>state</b> prop2: {myState.prop2}</div>
<button onClick={changeState}>Change Table state</button>
<button onClick={changeProps}>Change props that comes from parent</button>
</React.Fragment>
);
}
ReactDOM.render(<App/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
我在 遇到类似问题 时遇到了这个问题,但对 cbdevelopers 对他使用 useRef
的回答不满意,因为我觉得你应该不需要这个。 reactflux 的一位友好人士指出了一个更优雅的解决方案:
const Table = (props) => {
const { initialSortDirection, initialSortParam, initialSortEnabled } = props;
const [currentSortData, setSortData] = useState({
sortDirection: initialSortDirection,
sortParam: initialSortParam,
hasSort: initialSortEnabled
});
useEffect(() => {
setSortData((prevSortData) => ({
...prevSortData,
sortDirection: initialSortDirection
}));
}, [initialSortDirection]);
useEffect((prevSortData) => {
setSortData(() => ({
...prevSortData,
sortParam: initialSortParam
});
}, [initialSortParam]);
useEffect(() => {
setSortData((prevSortData) => ({
...prevSortData,
hasSort: initialSortEnabled
}));
}, [initialSortEnabled]);
return (<SomeComponent onChangeSort={setSortData} />)
}
我知道您想将所有内容合并为一个,但我不推荐这样做。 您想分离关注点,在道具更新时始终触发正确的效果。
https://reactjs.org/docs/hooks-effect.html#tip-use-multiple-effects-to-separate-concerns
请注意,OP 的解决方案是假的,好像两个道具同时更新,使用多个效果时只会保留最后一个状态变化。
希望这对某人有所帮助。