Reputation: 140
I have this action
export function fetchBranches() {
return async dispatch => {
const result = await axios.get('https://localhost:5002/Branches')
dispatch({ type: FETCH_BRANCHES, payload: result.data.value })
}
}
and such reducer
export const branchReducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_BRANCHES: {
return { ...state, branches: action.payload }
}
default: return state
}
}
In my component, I'm try to do such thing
const dispatch = useDispatch()
dispatch(fetchBranches())
const branches = useSelector(state => state.branches.branches)
return <>
some component that uses branches
</>
So my problem is i'm getting infinite number of request trying to fetch (I can see them in redux dev tools). My page are not getting updated, but if go to other page and then return to one that tries perform this fetch, I'm can see values in store and at the page. So questions are:
UPD:
Thanks a lot for your answers, but I still see behavior that my component rendered before I received data from api. I wanted to try useState
and set state in useEffect
, but I can't use useSelector
. What should I do to re-render component as soon as my data loaded?
UPD2: now my component look like
function BranchList() {
const [isLoaded, setIsLoaded] = useState(false)
const dispatch = useDispatch()
useEffect(() => {
dispatch(fetchBranches())
setIsLoaded(true)
}, [])
const branches = useSelector(state => state.branches.branches)
const headerNames = ["Id", "Name"]
if (!isLoaded) {
return <>Loading...</>
}
return (
<EditableTable
data={branches}
headerNames={headerNames}
onEditConfirm={(row) => dispatch(updateBranch(row))}
onDelete={(id) => dispatch(deleteBranch(id))} />
)
}
Upvotes: 1
Views: 121
Reputation: 26
You should try putting your dispatch action into an useEffect, so the code will be only executed once like so:
const dispatch = useDispatch()
React.useEffect(() => {
dispatch(fetchBranches())
}, [])
Documentation here.
Upvotes: 1
Reputation: 525
Perhaps if you try this:
function BranchList() {
const [isLoaded, setIsLoaded] = useState(false)
const dispatch = useDispatch()
useEffect(() => {
if(!isLoaded) {
dispatch(fetchBranches())
.then(() => setIsLoaded(true))
}
}, [isLoaded])
const branches = useSelector(state => state.branches.branches)
const headerNames = ["Id", "Name"]
if (!isLoaded) {
return <>Loading...</>
}
return (
<EditableTable
data={branches}
headerNames={headerNames}
onEditConfirm={(row) => dispatch(updateBranch(row))}
onDelete={(id) => dispatch(deleteBranch(id))} />
)
}
Upvotes: 1
Reputation: 44086
Dispatching generally should never be done directly in render, but in a useEffect
or an event callback.
In your case,
const dispatch = useDispatch()
useEffect(() => {
dispatch(fetchBranches());
},
[dispatch]
)
const branches = useSelector(state => state.branches.branches)
Also please note that you are writing a pretty old style of redux here - please read the official tutorials to learn the recommended "modern" style of redux we are officially recommending. You'll end up writing a lot less code.
Upvotes: 2
Reputation: 2475
dispatch
an action
into useEffect
hook to solve your issue.
Try:
useEffect(() => dispatch(fetchBranches()), [])
Upvotes: 1
Reputation: 21
Your HTTP request action causes side effects in the component. Every state change causes re-rendering the component. To avoid side effects, you should use useEffect hook in your component.
In your component,
const dispatch = useDispatch()
const onFetchBranches = useCallback(() => dispatch(fetchBranches()), [dispatch]);
const branches = useSelector(state => state.branches.branches)
useEffect(() => {
onFetchBranches();
}, [onFetchBranches]);
return <>
some component that uses branches
</>
You should check Reactjs documentation to understand useEffect hook better. https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects
Upvotes: 1