After Logging Back Again React Application Dispatches Actions Twice
To first clarify the existing behavior, the STATUS_FETCHING activeness was actually only being "dispatched" (i.e. if y'all exercise a console.log
right earlier the acceleration
phone call in getData
within useApiCall.js
) in one case, but the reducer code was executing twice.
I probably wouldn't have known what to expect for to explain why if it hadn't been for my research when writing this somewhat-related answer: React hook rendering an extra time.
You'll observe the post-obit block of code from React shown in that respond:
var currentState = queue.eagerState; var _eagerState = _eagerReducer(currentState, action); // Stash the eagerly computed country, and the reducer used to compute // it, on the update object. If the reducer hasn't changed past the // time nosotros enter the render stage, then the eager state can be used // without calling the reducer again. _update2.eagerReducer = _eagerReducer; _update2.eagerState = _eagerState; if (is(_eagerState, currentState)) { // Fast path. We tin can bond out without scheduling React to re-return. // It's even so possible that nosotros'll demand to rebase this update later, // if the component re-renders for a different reason and by that // time the reducer has inverse. return; }
In item, detect the comments indicating React may have to redo some of the piece of work if the reducer has inverse. The issue is that in your useApiCallReducer.js
y'all were defining your reducer inside of your useApiCallReducer
custom claw. This ways that on a re-render, you provide a new reducer function each time even though the reducer code is identical. Unless your reducer needs to use arguments passed to the custom hook (rather than just using the state
and action
arguments passed to the reducer), you should define the reducer at the outer level (i.e. not nested inside some other function). In general, I would recommend avoiding defining a office nested within another unless it actually uses variables from the scope it is nested within.
When React sees the new reducer subsequently the re-render, it has to throw out some of the work it did earlier when trying to determine whether a re-render would be necessary because your new reducer might produce a different result. This is all just part of functioning optimization details in the React code that you more often than not don't need to worry about, but information technology is worth being aware that if you redefine functions unnecessarily, you may stop upwards defeating some performance optimizations.
To solve this I changed the following:
import { useReducer } from "react"; import types from "./types"; const initialState = { data: [], fault: [], status: types.STATUS_IDLE }; consign function useApiCallReducer() { office reducer(state, activeness) { console.log("prevState: ", state); console.log("action: ", action); switch (action.blazon) { case types.STATUS_FETCHING: return { ...state, status: types.STATUS_FETCHING }; instance types.STATUS_FETCH_SUCCESS: return { ...land, error: [], data: action.data, condition: types.STATUS_FETCH_SUCCESS }; instance types.STATUS_FETCH_FAILURE: render { ...state, error: activeness.error, status: types.STATUS_FETCH_FAILURE }; default: return land; } } return useReducer(reducer, initialState); }
to instead be:
import { useReducer } from "react"; import types from "./types"; const initialState = { information: [], mistake: [], status: types.STATUS_IDLE }; function reducer(state, action) { console.log("prevState: ", state); console.log("action: ", action); switch (activeness.type) { example types.STATUS_FETCHING: render { ...land, status: types.STATUS_FETCHING }; example types.STATUS_FETCH_SUCCESS: return { ...state, mistake: [], data: activity.information, status: types.STATUS_FETCH_SUCCESS }; instance types.STATUS_FETCH_FAILURE: return { ...state, error: action.error, status: types.STATUS_FETCH_FAILURE }; default: return state; } } export office useApiCallReducer() { return useReducer(reducer, initialState); }
Here's a related answer for a variation on this problem when the reducer has dependencies (eastward.thou. on props or other land) that require it to exist defined within another function: React useReducer Hook fires twice / how to pass props to reducer?
Beneath is a very contrived example to demonstrate a scenario where a change in the reducer during return requires it to be re-executed. You can run into in the console, that the kickoff time y'all trigger the reducer via i of the buttons, it executes twice -- once with the initial reducer (addSubtractReducer) and and so over again with the different reducer (multiplyDivideReducer). Subsequent dispatches seem to trigger the re-render unconditionally without outset executing the reducer, so but the correct reducer is executed. You lot can see particularly interesting beliefs in the logs if you lot beginning dispatch the "nochange" action.
import React from "react"; import ReactDOM from "react-dom"; const addSubtractReducer = (country, { blazon }) => { allow newState = state; switch (type) { case "increase": newState = country + 10; break; example "subtract": newState = land - 10; break; default: newState = country; } panel.log("add/subtract", type, newState); return newState; }; const multiplyDivideReducer = (state, { type }) => { allow newState = state; switch (type) { case "increase": newState = state * 10; break; case "subtract": newState = state / ten; pause; default: newState = state; } panel.log("multiply/divide", type, newState); return newState; }; office App() { const reducerIndexRef = React.useRef(0); React.useEffect(() => { reducerIndexRef.current += 1; }); const reducer = reducerIndexRef.electric current % 2 === 0 ? addSubtractReducer : multiplyDivideReducer; const [reducerValue, dispatch] = React.useReducer(reducer, 10); return ( <div> Reducer Value: {reducerValue} <div> <push button onClick={() => dispatch({ type: "increase" })}>Increase</button> <button onClick={() => acceleration({ type: "decrease" })}>Decrease</button> <button onClick={() => dispatch({ type: "nochange" })}> Dispatch With No Change </button> </div> </div> ); } const rootElement = certificate.getElementById("root"); ReactDOM.render(<App />, rootElement);
veachuntoonesch1968.blogspot.com
Source: https://stackoverflow.com/questions/54892403/usereducer-action-dispatched-twice
0 Response to "After Logging Back Again React Application Dispatches Actions Twice"
Post a Comment