如何比较钩子中react redux状态的值



我最近使用钩子写了一个表组件,每次页面加载都有一个API调用到后端,所以同时有一个加载Spinner将被显示,直到有一个来自API的响应。我使用redux作为状态管理,所以当有来自API的响应时,就会发出一个动作并更新状态。这里的问题是,通常在类组件中我们可以使用

来比较prevProps和nextProps
componentDidUpdate(prevProps){
if(this.props.someState !== prevProps.someState){
// do something
}
}

,但我不知道如何实现相同的使用钩子。我也提到了这个stackoverflow问题如何比较oldValues和newValues对React钩子使用效果?

但是这个解决方案似乎在我的情况下不起作用。我尝试创建自定义钩子usePrevious和创建一个ref来比较当前值

这是我的部分代码。

let loading = true;
let tableData = useSelector((state) => {

if (
state.common.tableDetails.data &&
state.common.tableDetails.status === true
) {
loading = false;
return state.common.tableDetails.data;
}

if (
state.common.tableDetails.data &&
state.common.tableDetails.status === false

) {
loading = true;
}
return [];
});
// table component
<Fragment>
{ 
loading === true ? <Spinner />  :  <TableComponent tableData={tableData }/>
}
</Fragment>

所以每当组件加载时,如果在redux状态中存在任何数据,该数据就会显示,并且不会对prevProps和nextProps进行比较,因为加载旋转器不会显示,并且在新调用的api状态响应后将更新,新数据将显示在表中。

更新1:下面是分派、动作和减速器的代码

useEffect(() => {

dispatch(fetchDetails(params.someID));
}, [dispatch, params.someID]);

操作文件

export const fetchDetails = (data) => (dispatch) => {
axios
.post(
`${SomeURL}/fetchAll`,
data
)
.then((res) => {
if (res.data.status) {
dispatch({
type: FETCH_DETAILS,
payload: res.data,
});
}
})
.catch((err) => console.log(err));
};

减速器文件

const initialState = {
tableDetails: {},
};
export default function (state = initialState, action) {
switch (action.type) {
case FETCH_DETAILS:
return {
...state,
tableDetails: action.payload,
};
default:
return state;
}
}

您可以实现简单的验证来达到您想要的效果。我假设您的tableData有一个id,那么您可以这样验证:

useEffect(() => {
// don't fetch the same table
if(tableData?.id !== params.someID {
dispatch(fetchDetails(params.someID));
}
}, [dispatch, params.someID, tableData?.id]);

更新1

要比较以前的表数据和新的表数据,可以在action creator中这样做:

  1. 移动loading状态到redux store
  2. 使用getState函数,这是动作创建者收到的第二个参数(假设您正在使用redux-thunk)
  3. 在调度动作之前,将新的tableData与存储中的数据(之前的tableData)进行比较。
  4. 检查并更新loading状态

export const fetchDetails = (data) => (dispatch, getState) => {
axios
.post(
`${SomeURL}/fetchAll`,
data
)
.then((res) => {
if (res.data.status) {
const prevTableData = getState().tableData;

// compare prev and new table data
if(isDiff(prevTableData, res.data) {
// Do something...
}
dispatch({
type: FETCH_DETAILS,
payload: res.data,
});
}
})
.catch((err) => console.log(err));
};

正确的方法是使用reduxreselect包https://github.com/reduxjs/reselect。Redux团队推荐。

reelect是专门为这种情况设计的。它还在底层使用了记忆。你可以自定义选择器,并在这样的场景中使用它们。

你可以为所有的重选函数创建一个单独的文件,像这样

//select file (eg: selectors.js)
import { createSelector } from "reselect";
const tableDetails = state => state.common.tableDetails;
function checkDataFunction(details){
if (
details.data &&
details.status === true
) {
return {
loading: false,
data: details.data
}
}

else if (
details.data &&
details.status === false

) {
return {
loading: true,
data: []
}
}
return {
loading: true,
data: []
};
}
//redux selector
//enables you to make your own custom selector
export const checkData = createSelector(
tableDetails,
checkDataFunction
);

现在在你想要使用这个的主组件文件中,只需导入它并像其他选择器一样使用

//main component file (eg: App.js)
import {checkData} from './selectors';

export default function App(){
const tableData = useSelector(checkData);
useEffect(() => {
//continue with your logic here
},[tableData.loading])
//...
}

相关内容

  • 没有找到相关文章

最新更新