简体   繁体   中英

React and redux usage with async data fetching

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:

  1. Where and how should I dispatch actions to fetch some data to render it then?
  2. Why I'm getting that much requests and how can I fix it?

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))} />
    )
  }

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.

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 .

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

dispatch an action into useEffect hook to solve your issue.

Try:

useEffect(() => dispatch(fetchBranches()), [])

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))} />
    )
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM