I have recently written a table component 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 component we can compare prevProps and nextProps using
componentDidUpdate(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 compare 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 compare current value ,this didn't solve my issue.
Here's my part of code.
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>
So whenever the component loads, if there is any data present in redux state , that data is shown and no comparison 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;
}
}
Redux and React Hooks should be seen as complements and also as different things. While with the new React Hooks additions, useContext and useReducer, you can manage the global state, in projects with larger complexity you can rely on Redux to help you manage the application data.
We recommend using the React-Redux hooks API as the default approach in your React components. The existing connect API still works and will continue to be supported, but the hooks API is simpler and works better with TypeScript. These hooks were first added in v7. 1.0.
While there's currently no React Hook that does this out of the box, you can manually retrieve either the previous state or props from within a functional component by leveraging the useRef , useState , usePrevious , and useEffect Hooks in React.
Redux uses shallow equality checking in its combineReducers function to return either a new mutated copy of the root state object, or, if no mutations have been made, the current root state object.
React also lets us write custom hooks, which let us extract reusable hooks to add our own behavior on top of React's built-in hooks. React Redux includes its own custom hook APIs, which allow your React components to subscribe to the Redux store and dispatch actions.
React Redux includes its own custom hook APIs, which allow your React components to subscribe to the Redux store and dispatch actions.
Introduced on ReactJS version 16.8, Hooks are functions that allow you to use some features of React, like state, without writing a class. This feature favors a functional approach when working with React, creating components, etc. Though rumored to do so in the community, classes are not being replaced in React.
As with connect (), you should start by wrapping your entire application in a <Provider> component to make the store available throughout the component tree: From there, you may import any of the listed React Redux hooks APIs and use them within your function components. const result: any = useSelector(selector: Function, equalityFn?:
You 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]);
To compare previous table data to new feched one you could do that in the action creator:
loading
state to redux store.getState
function which is the second argument recived by action creator (assuming that you are using redux-thunk)tableData
with data in the store(prev tableData
).loading
state.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));
};
The right way is to use redux reselect package https://github.com/reduxjs/reselect. Redux team recommends 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.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
);
Now in your main component file where you want to use this just import it and use it as any other selector
//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])
//...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With