useReducer 액션이 두 번 디스패치되었습니다.
시나리오
작업을 반환하는 사용자 지정 후크가 있습니다.상위 구성 요소 "컨테이너"는 사용자 지정 후크를 사용하여 작업을 하위 구성 요소에 소품으로 전달합니다.
문제
하위 구성 요소에서 작업을 실행하면 실제 디스패치가 두 번 수행됩니다.이제 자녀가 직접 후크를 사용하여 액션을 호출한 경우 디스패치는 한 번만 실행됩니다.
재생 방법:
아래 샌드박스를 열고 chrome에서 devtools를 열면 추가한 콘솔 로그를 볼 수 있습니다.
https://codesandbox.io/s/j299ww3lo5?fontsize=14
Main.js(자녀 컴포넌트)는 propos.actions.getData()를 호출합니다.
DevTools에서 로그를 지웁니다.미리보기에서 폼에 값을 입력하고 버튼을 클릭합니다.콘솔 로그에는 redex-logger 등의 액션이 표시되며 상태를 변경하지 않고 STATUS_FETCHING 액션이 두 번 실행된다는 것을 알 수 있습니다.
Main.js로 이동하여 9행과 10행의 코멘트를 입력합니다.현재는 기본적으로 커스텀 훅을 직접 소비하고 있습니다.
DevTools에서 로그를 지웁니다.미리보기에서 폼에 값을 입력하고 버튼을 클릭합니다.콘솔 로그에는 STATUS_FETCHING이 한 번만 실행되어 그에 따라 상태가 변경됩니다.
명백한 성능 불이익은 없지만, 왜 그런 일이 일어나는지 이해할 수 없습니다.훅스에 너무 집중해서 바보 같은 걸 놓쳤나 봐이 퍼즐에서 날 풀어줘감사합니다!
기존 동작을 하기 dispatched)되어 (즉, STATUS_FETCHING을 ).되었을 뿐입니다(즉,console.log
의 the dispatch
getData
의 범위 내에서useApiCall.js
)가 1회 실행되었지만 리듀서 코드가 2회 실행되었습니다.
이 다소 관련이 있는 답변을 쓸 때 내 연구가 아니었다면, 나는 무엇을 찾아야 할지 몰랐을 것이다: 리액트 후크 렌더링(react hook rendering) 추가 시간.
이 답변에 표시된 React의 코드 블록은 다음과 같습니다.
var currentState = queue.eagerState;
var _eagerState = _eagerReducer(currentState, action);
// Stash the eagerly computed state, and the reducer used to compute
// it, on the update object. If the reducer hasn't changed by the
// time we enter the render phase, 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 can bail out without scheduling React to re-render.
// It's still possible that we'll need to rebase this update later,
// if the component re-renders for a different reason and by that
// time the reducer has changed.
return;
}
특히 리덕터가 변경된 경우 React가 일부 작업을 다시 수행해야 할 수 있음을 나타내는 코멘트에 유의하십시오.이 문제는 이 모든 것이useApiCallReducer.js
하고 있었습니다.useApiCallReducer
에서는 매번 입니다.즉, 리듀서 코드가 동일하더라도 리듀서 함수를 매번 새로 제공합니다.가 없는 단, ""를 하는 것 만이 ),state
★★★★★★★★★★★★★★★★★」action
reducer에 전달되는 인수), 외부 수준에서 reducer를 정의해야 합니다(즉, 다른 함수 내부에 중첩되지 않음).일반적으로 함수가 내포된 범위의 변수를 실제로 사용하지 않는 한 다른 함수에 내포된 함수를 정의하는 것을 피하는 것이 좋습니다.
React는 리렌더 후에 새로운 리듀서를 발견하면 새로운 리듀서가 다른 결과를 가져올 수 있기 때문에 리렌더가 필요한지 여부를 판단하기 위해 이전에 했던 작업 중 일부를 포기해야 합니다.이 모든 것은 React 코드에 포함된 성능 최적화 세부 사항의 일부에 불과하며, 이는 대부분 걱정할 필요가 없습니다. 그러나 함수를 불필요하게 재정의할 경우 성능 최적화가 실패할 수 있습니다.
이 문제를 해결하기 위해 다음을 변경했습니다.
import { useReducer } from "react";
import types from "./types";
const initialState = {
data: [],
error: [],
status: types.STATUS_IDLE
};
export function useApiCallReducer() {
function reducer(state, action) {
console.log("prevState: ", state);
console.log("action: ", action);
switch (action.type) {
case types.STATUS_FETCHING:
return {
...state,
status: types.STATUS_FETCHING
};
case types.STATUS_FETCH_SUCCESS:
return {
...state,
error: [],
data: action.data,
status: types.STATUS_FETCH_SUCCESS
};
case types.STATUS_FETCH_FAILURE:
return {
...state,
error: action.error,
status: types.STATUS_FETCH_FAILURE
};
default:
return state;
}
}
return useReducer(reducer, initialState);
}
대신 다음과 같습니다.
import { useReducer } from "react";
import types from "./types";
const initialState = {
data: [],
error: [],
status: types.STATUS_IDLE
};
function reducer(state, action) {
console.log("prevState: ", state);
console.log("action: ", action);
switch (action.type) {
case types.STATUS_FETCHING:
return {
...state,
status: types.STATUS_FETCHING
};
case types.STATUS_FETCH_SUCCESS:
return {
...state,
error: [],
data: action.data,
status: types.STATUS_FETCH_SUCCESS
};
case types.STATUS_FETCH_FAILURE:
return {
...state,
error: action.error,
status: types.STATUS_FETCH_FAILURE
};
default:
return state;
}
}
export function useApiCallReducer() {
return useReducer(reducer, initialState);
}
다음은 환원기가 다른 기능 내에서 정의되어야 하는 종속성(예: 지지대 또는 기타 상태에 대한)이 있을 때 이 문제에 대한 변동에 대한 관련 대답이다.리액션 useReducer Hook은 두 번 작동합니까? / 어떻게 하면 소품을 리덕터에 전달할 수 있습니까?
다음은 렌더 중 리듀서를 변경하기 위해 리듀서를 재실행해야 하는 시나리오를 보여 주는 매우 정교한 예입니다.콘솔에서 버튼 중 하나를 사용하여 처음 리덕터를 트리거하면 리덕터가 두 번 실행됩니다.첫 번째 리덕터(addSubtractReducer)와 다른 리덕터(multiplyDivideReducer)를 사용하여 다시 실행됩니다.후속 디스패치에서는 먼저 리덕터를 실행하지 않고 무조건 리덕터를 트리거하는 것처럼 보이므로 올바른 리덕터만 실행됩니다.처음에 "nochange" 액션을 디스패치하면 로그에서 특히 흥미로운 동작을 볼 수 있습니다.
import React from "react";
import ReactDOM from "react-dom";
const addSubtractReducer = (state, { type }) => {
let newState = state;
switch (type) {
case "increase":
newState = state + 10;
break;
case "decrease":
newState = state - 10;
break;
default:
newState = state;
}
console.log("add/subtract", type, newState);
return newState;
};
const multiplyDivideReducer = (state, { type }) => {
let newState = state;
switch (type) {
case "increase":
newState = state * 10;
break;
case "decrease":
newState = state / 10;
break;
default:
newState = state;
}
console.log("multiply/divide", type, newState);
return newState;
};
function App() {
const reducerIndexRef = React.useRef(0);
React.useEffect(() => {
reducerIndexRef.current += 1;
});
const reducer =
reducerIndexRef.current % 2 === 0
? addSubtractReducer
: multiplyDivideReducer;
const [reducerValue, dispatch] = React.useReducer(reducer, 10);
return (
<div>
Reducer Value: {reducerValue}
<div>
<button onClick={() => dispatch({ type: "increase" })}>Increase</button>
<button onClick={() => dispatch({ type: "decrease" })}>Decrease</button>
<button onClick={() => dispatch({ type: "nochange" })}>
Dispatch With No Change
</button>
</div>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
를제 remove remove remove를 remove remove 를 remove 。<React.StrictMode>
문제가 해결됩니다.
★★★★★★★★★를 사용하고 있는 경우는React.StrictMode
React는 환원제의 순도를 테스트하기 위해 동일한 인수를 사용하여 환원제를 여러 번 호출합니다.Strict Mode를 디세블로 하면 리듀서가 올바르게 메모되어 있는지 테스트할 수 있습니다.
https://github.com/facebook/react/issues/16295#issuecomment-610098654 에서 :
"문제"는 없습니다.리액션은 예상치 못한 부작용을 더 명확하게 하기 위해 의도적으로 당신의 환원제를 두 번 호출합니다.리듀서는 순수하기 때문에 두 번 불러도 어플리케이션의 로직에는 영향을 주지 않습니다.그러니까 걱정하지 마세요.
프로덕션에서는 한 번만 호출됩니다.
React 문서의 설명대로:
strict 모드는 부작용을 자동으로 검출할 수 없지만, 조금 더 결정적으로 만들어 부작용을 검출하는 데 도움이 됩니다.이 작업은 의도적으로 다음 함수를 이중 호출하여 수행됩니다. [...] useState, useMemo 또는 useReducer로 전달된 함수
이는 리덕터가 순수해야 하며 매번 동일한 인수로 동일한 출력을 제공해야 하며 리액트 엄격 모드에서는 리덕터를 두 번 호출하여 자동으로 테스트(경우에 따라)하기 때문입니다.
이것은 개발에만 한정되는 행동이기 때문에 문제될 것이 없기 때문에 그만두는 것은 추천하지 않습니다.<React.StrictMode>
키, sideEffects 등과 관련된 많은 문제를 강조하는데 매우 도움이 되기 때문입니다.
제거함으로써<React.StrictMode>
디스패치는 여러 번 호출되지 않습니다.저도 이 문제에 직면해 있었기 때문에 이 솔루션은 효과가 있었습니다.
이것은,<React>를 삭제해 주세요.Strict Mode > 디스패치는 여러 번 호출되지 않습니다.저도 이 문제에 직면해 있었기 때문에 이 솔루션은 효과가 있었습니다.
언급URL : https://stackoverflow.com/questions/54892403/usereducer-action-dispatched-twice
'programing' 카테고리의 다른 글
스프링 - 로컬 파일 시스템에 저장하지 않고 대용량 멀티파트 파일 업로드를 데이터베이스로 스트리밍하는 방법 (0) | 2023.03.15 |
---|---|
WordPress에서 자동으로 페이지 생성 (0) | 2023.03.15 |
잘못된 디코딩으로 인해 특수 문자에 대한 워드프레스 검색에 실패했습니다. (0) | 2023.03.15 |
TypeScript에서는 강력한 타입의 함수를 파라미터로 사용할 수 있습니까? (0) | 2023.03.15 |
Spring RestTemplate 시간 초과 (0) | 2023.03.10 |