I have recently written a table ponent using hooks , and every time page loads there is an API call to backend, so meanwhile there is a loading Spinner will be shown until there is an response from the API. I'm using redux as state management, so when there is a response from API , an action is dispatched and state is updated. So the problem here is ,usually in class ponent we can pare prevProps and nextProps using
ponentDidUpdate(prevProps){
if(this.props.someState !== prevProps.someState){
// do something
}
}
but i'm not sure how to achieve the same using Hooks. I also referred this stackoverflow question How to pare oldValues and newValues on React Hooks useEffect?
but this solution doesn't seem to be working in my case . I did try creating custom hook usePrevious and creating a ref to pare current value ,this didn't solve my issue.
Here's my part of code.
let loading = true;
let tableData = useSelector((state) => {
if (
statemon.tableDetails.data &&
statemon.tableDetails.status === true
) {
loading = false;
return statemon.tableDetails.data;
}
if (
statemon.tableDetails.data &&
statemon.tableDetails.status === false
) {
loading = true;
}
return [];
});
// table ponent
<Fragment>
{
loading === true ? <Spinner /> : <TableComponent tableData={tableData }/>
}
</Fragment>
So whenever the ponent loads, if there is any data present in redux state , that data is shown and no parison is done for prevProps and nextProps because of which Loading spinner won't show up and after a response of newly called api state will be update and new data will be shown in Table.
UPDATE 1: Here's the code for dispatch and action and reducer
useEffect(() => {
dispatch(fetchDetails(params.someID));
}, [dispatch, params.someID]);
Action File
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));
};
Reducer File
const initialState = {
tableDetails: {},
};
export default function (state = initialState, action) {
switch (action.type) {
case FETCH_DETAILS:
return {
...state,
tableDetails: action.payload,
};
default:
return state;
}
}
I have recently written a table ponent using hooks , and every time page loads there is an API call to backend, so meanwhile there is a loading Spinner will be shown until there is an response from the API. I'm using redux as state management, so when there is a response from API , an action is dispatched and state is updated. So the problem here is ,usually in class ponent we can pare prevProps and nextProps using
ponentDidUpdate(prevProps){
if(this.props.someState !== prevProps.someState){
// do something
}
}
but i'm not sure how to achieve the same using Hooks. I also referred this stackoverflow question How to pare oldValues and newValues on React Hooks useEffect?
but this solution doesn't seem to be working in my case . I did try creating custom hook usePrevious and creating a ref to pare current value ,this didn't solve my issue.
Here's my part of code.
let loading = true;
let tableData = useSelector((state) => {
if (
state.mon.tableDetails.data &&
state.mon.tableDetails.status === true
) {
loading = false;
return state.mon.tableDetails.data;
}
if (
state.mon.tableDetails.data &&
state.mon.tableDetails.status === false
) {
loading = true;
}
return [];
});
// table ponent
<Fragment>
{
loading === true ? <Spinner /> : <TableComponent tableData={tableData }/>
}
</Fragment>
So whenever the ponent loads, if there is any data present in redux state , that data is shown and no parison is done for prevProps and nextProps because of which Loading spinner won't show up and after a response of newly called api state will be update and new data will be shown in Table.
UPDATE 1: Here's the code for dispatch and action and reducer
useEffect(() => {
dispatch(fetchDetails(params.someID));
}, [dispatch, params.someID]);
Action File
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));
};
Reducer File
const initialState = {
tableDetails: {},
};
export default function (state = initialState, action) {
switch (action.type) {
case FETCH_DETAILS:
return {
...state,
tableDetails: action.payload,
};
default:
return state;
}
}
Share
Improve this question
edited Aug 26, 2021 at 4:26
jarivak
asked Aug 23, 2021 at 16:30
jarivakjarivak
8581 gold badge14 silver badges28 bronze badges
1
- Have you tried using React.memo? – Nirmalya Ghosh Commented Aug 23, 2021 at 17:10
2 Answers
Reset to default 3 +50You can implement simple validation to achieve what you want.
I will assume your tableData
has an id
, then you can validate like this:
useEffect(() => {
// don't fetch the same table
if(tableData?.id !== params.someID {
dispatch(fetchDetails(params.someID));
}
}, [dispatch, params.someID, tableData?.id]);
UPDATE 1
To pare previous table data to new feched one you could do that in the action creator:
- move
loading
state to redux store. - use
getState
function which is the second argument recived by action creator (assuming that you are using redux-thunk) - before dispatching the action, pare new
tableData
with data in the store(prevtableData
). - do your check and update
loading
state.
export const fetchDetails = (data) => (dispatch, getState) => {
axios
.post(
`${SomeURL}/fetchAll`,
data
)
.then((res) => {
if (res.data.status) {
const prevTableData = getState().tableData;
// pare prev and new table data
if(isDiff(prevTableData, res.data) {
// Do something...
}
dispatch({
type: FETCH_DETAILS,
payload: res.data,
});
}
})
.catch((err) => console.log(err));
};
The right way is to use redux reselect package https://github./reduxjs/reselect. Redux team remends it.
reselect is designed specifically for this kind of scenarios. It also uses memoization under the hood. You can make your custom selectors and use them in scenarios like this.
You can make a seperate file for all the reselect functions like this
//select file (eg: selectors.js)
import { createSelector } from "reselect";
const tableDetails = state => state.mon.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
);
Now in your main ponent file where you want to use this just import it and use it as any other selector
//main ponent file (eg: App.js)
import {checkData} from './selectors';
export default function App(){
const tableData = useSelector(checkData);
useEffect(() => {
//continue with your logic here
},[tableData.loading])
//...
}