반응 후크 사용효과의 종속성 누락: 'displatch'
reaction js로 작업하는 것은 이번이 처음인데, 이 뷰를 떠날 때 경고를 제거하려고 하는 이유는 다른 뷰에 표시하기 싫지만 오류가 없을 경우 성공 경고를 계속 유지하여 다른 뷰로 리디렉션할 때 표시하기를 원하기 때문이다.
하지만 나는 구글 크롬에 이 마모된 것을 얻는다.Line 97:6: React Hook useEffect has a missing dependency: 'dispatch'. Either include it or remove the dependency array react-hooks/exhaustive-deps
만약 내가 디스패치를 포함한다면 나는 무한루프를 얻을 수 있다.
const [state, dispatch] = useUserStore();
useEffect(() => {
let token = params.params.token;
checktoken(token, dispatch);
}, [params.params.token]);
useEffect(() => {
return () => {
if (state.alert.msg === "Error") {
dispatch({
type: REMOVE_ALERT
});
}
};
}, [state.alert.msg]);
//response from the api
if (!token_valide || token_valide_message === "done") {
return <Redirect to="/login" />;
}
useUserStore 입니다.
const globalReducers = useCombinedReducers({
alert: useReducer(alertReducer, alertInitState),
auth: useReducer(authReducer, authInitState),
register: useReducer(registerReducer, registerInitState),
token: useReducer(passeditReducer, tokenvalidationInitState)
});
return (
<appStore.Provider value={globalReducers}>{children}</appStore.Provider>
);
};
export const useUserStore = () => useContext(appStore);
업데이트 09/11/2020
이 솔루션은 더 이상 필요 없음eslint-plugin-react-hooks@4.1.0
그 이상
지금useMemo
그리고useCallback
참조 유형을 종속성으로 안전하게 수신할 수 있다.#19590
function MyComponent() {
const foo = ['a', 'b', 'c']; // <== This array is reconstructed each render
const normalizedFoo = useMemo(() => foo.map(expensiveMapper), [foo]);
return <OtherComponent foo={normalizedFoo} />
}
콜백을 안전하게 안정화하는 방법(정상화)의 또 다른 예가 여기에 있다.
const Parent = () => {
const [message, setMessage] = useState('Greetings!')
return (
<h3>
{ message }
</h3>
<Child setter={setMessage} />
)
}
const Child = ({
setter
}) => {
const stableSetter = useCallback(args => {
console.log('Only firing on mount!')
return setter(args)
}, [setter])
useEffect(() => {
stableSetter('Greetings from child\'s mount cycle')
}, [stableSetter]) //now shut up eslint
const [count, setCount] = useState(0)
const add = () => setCount(c => c + 1)
return (
<button onClick={add}>
Rerender {count}
</button>
)
}
이제 다음에서 입증된 것과 같이 안정적인 서명이 있는 참조 유형useState
또는useDispatch
트리거 없이 효과 내부에서 안전하게 사용 가능exhaustive-deps
와도props
---
구답
dispatch
풍습에서 유래하다hook
따라서 안정적인 서명이 없으므로 렌더별로 변경된다(참조 평등).처리기를 내부로 감싸서 종속성의 이전 계층 추가useCallback
갈고리를 걸다
const [foo, dispatch] = myCustomHook()
const stableDispatch = useCallback(dispatch, []) //assuming that it doesn't need to change
useEffect(() =>{
stableDispatch(foo)
},[stableDispatch])
useCallback
그리고useMemo
동기성을 보장하기 위해 종속성 검사 계층을 추가하기 위한 주요 목적을 가진 도우미 후크.보통 당신은 함께 일하고 싶어한다.useCallback
에 대한 안정적인 서명을 보장하기 위해prop
어떻게 바뀔지 알고 반응하지 않는지 말이야
A function
(참조 유형)을 통해 전달됨props
예를 들면
const Component = ({ setParentState }) =>{
useEffect(() => setParentState('mounted'), [])
}
uppon 마운팅이 상위(일반적이지 않음)에서 일부 상태를 설정해야 하는 하위 구성 요소가 있다고 가정해 봅시다. 위의 코드는 다음에서 보고되지 않은 종속성에 대한 경고를 생성함useEffect
, 그러니까 선언하자.setParentState
대응으로 확인할 종속성
const Component = ({ setParentState }) =>{
useEffect(() => setParentState('mounted'), [setParentState])
}
이제 이 효과는 각 렌더에 대해 실행되며, 마운팅뿐만 아니라 각 업데이트에도 적용된다.때문에 이런 일이 일어난다.setParentState
a이다function
매번 그 함수가 재현되는 것Component
부름을 받다당신도 아시잖아요.setParentState
시간외 근무 수당은 바뀌지 않을 테니 리액션이라고 말해도 안전해원래 도우미를 내부로 감싸서useCallback
정확히 그렇게 하고 있다(다른 종속성 검사 계층에 추가).
const Component = ({ setParentState }) =>{
const stableSetter = useCallback(() => setParentState(), [])
useEffect(() => setParentState('mounted'), [stableSetter])
}
여기 있다.지금React
을 알고 있다stableSetter
라이프사이클 내부의 시그니처를 변경하지 않기 때문에 너무 불편하게 실행할 필요가 없다.
곁눈질로useCallback
이렇게 쓰이기도 한다.useMemo
을 선택하기 , 값비싼 수 있다
두 가지 useCallback
이다
참조 평등에 의존하는 하위 구성요소를 최적화하여 불필요한 렌더링을 방지하십시오.글꼴
값비싼 계산을 메모화한다.
근본적으로는 문제를 해결할 수 있다고 생각하지만, 그것은 useCombinedReducers를 변경한다는 것을 의미하며, 나는 useCombinedReducers가 호출할 때마다 발송을 위해 새로운 참조를 반환해서는 안 된다고 생각하기 때문에 repo를 요청하고 당김 요청을 만들었다.
function memoize(fn) {
let lastResult,
//initial last arguments is not going to be the same
// as anything you will pass to the function the first time
lastArguments = [{}];
return (...currentArgs) => {
//returning memoized function
//check if currently passed arguments are the same as
// arguments passed last time
const sameArgs =
currentArgs.length === lastArguments.length &&
lastArguments.reduce(
(result, lastArg, index) =>
result && Object.is(lastArg, currentArgs[index]),
true,
);
if (sameArgs) {
//current arguments are same as last so just
// return the last result and don't execute function
return lastResult;
}
//current arguments are not the same as last time
// or function called for the first time, execute the
// function and set last result
lastResult = fn.apply(null, currentArgs);
//set last args to current args
lastArguments = currentArgs;
//return result
return lastResult;
};
}
const createDispatch = memoize((...dispatchers) => action =>
dispatchers.forEach(fn => fn(action)),
);
const createState = memoize(combinedReducers =>
Object.keys(combinedReducers).reduce(
(acc, key) => ({ ...acc, [key]: combinedReducers[key][0] }),
{},
),
);
const useCombinedReducers = combinedReducers => {
// Global State
const state = createState(combinedReducers);
const dispatchers = Object.values(combinedReducers).map(
([, dispatch]) => dispatch,
);
// Global Dispatch Function
const dispatch = createDispatch(...dispatchers);
return [state, dispatch];
};
export default useCombinedReducers;
다음은 작업 예시:
const reduceA = (state, { type }) =>
type === 'a' ? { count: state.count + 1 } : state;
const reduceC = (state, { type }) =>
type === 'c' ? { count: state.count + 1 } : state;
const state = { count: 1 };
function App() {
const [a, b] = React.useReducer(reduceA, state);
const [c, d] = React.useReducer(reduceC, state);
//memoize what is passed to useCombineReducers
const obj = React.useMemo(
() => ({ a: [a, b], c: [c, d] }),
[a, b, c, d]
);
//does not do anything with reduced state
const [, reRender] = React.useState();
const [s, dispatch] = useCombinedReducers(obj);
const rendered = React.useRef(0);
const [sc, setSc] = React.useState(0);
const [dc, setDc] = React.useState(0);
rendered.current++;//display how many times this is rendered
React.useEffect(() => {//how many times state changed
setSc(x => x + 1);
}, [s]);
React.useEffect(() => {//how many times dispatch changed
setDc(x => x + 1);
}, [dispatch]);
return (
<div>
<div>rendered {rendered.current} times</div>
<div>state changed {sc} times</div>
<div>dispatch changed {dc} times</div>
<button type="button" onClick={() => reRender({})}>
re render
</button>
<button
type="button"
onClick={() => dispatch({ type: 'a' })}
>
change a
</button>
<button
type="button"
onClick={() => dispatch({ type: 'c' })}
>
change c
</button>
<pre>{JSON.stringify(s, undefined, 2)}</pre>
</div>
);
}
function memoize(fn) {
let lastResult,
//initial last arguments is not going to be the same
// as anything you will pass to the function the first time
lastArguments = [{}];
return (...currentArgs) => {
//returning memoized function
//check if currently passed arguments are the same as
// arguments passed last time
const sameArgs =
currentArgs.length === lastArguments.length &&
lastArguments.reduce(
(result, lastArg, index) =>
result && Object.is(lastArg, currentArgs[index]),
true
);
if (sameArgs) {
//current arguments are same as last so just
// return the last result and don't execute function
return lastResult;
}
//current arguments are not the same as last time
// or function called for the first time, execute the
// function and set last result
lastResult = fn.apply(null, currentArgs);
//set last args to current args
lastArguments = currentArgs;
//return result
return lastResult;
};
}
const createDispatch = memoize((...dispatchers) => action =>
dispatchers.forEach(fn => fn(action))
);
const createState = memoize(combinedReducers =>
Object.keys(combinedReducers).reduce(
(acc, key) => ({
...acc,
[key]: combinedReducers[key][0],
}),
{}
)
);
const useCombinedReducers = combinedReducers => {
// Global State
const state = createState(combinedReducers);
const dispatchers = Object.values(combinedReducers).map(
([, dispatch]) => dispatch
);
// Global Dispatch Function
const dispatch = createDispatch(...dispatchers);
return [state, dispatch];
};
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
최소 문제 사례, 2021년
문법: 은 이 있고 당신은 이 코드를 가지고 있고 그것은 무한의 루프를 만들어낸다. 왜하하면 되기 때문이다.array
은 어에 있다.setArray
매 순간마다useEffect
그것은 그것이 계속 설정될 것이라는 것을 의미한다.
const MyComponent = ({ removeValue }) => {
const [array, setArray] = useState([1, 2, 3, 4, 5]);
// useEffect to remove `removeValue` from array
useEffect(() => {
const newArray = array.filter((value) => value !== removeValue);
setArray(newArray);
}, [array, removeValue]);
return <div>{array.join(" ")}</div>;
};
다음과 같은 여러 솔루션이 있다.
솔루션 #1: 이전 상태 사용
그setState
또한 콜백을 받을 수 있는데, 이것은 당신에게 논쟁으로서 현재 상태를 제공한다.이것은 때때로 문제를 해결하는 데 사용될 수 있다.이제 다음을 포함하지 않아도 된다.array
종속성 배열에서:
const MyComponent = ({ removeValue }) => {
const [array, setArray] = useState([1, 2, 3, 4, 5]);
// useEffect to remove `removeValue` from array
useEffect(() => {
setArray((previousArray) => {
const newArray = previousArray.filter((value) => value !== removeValue);
return newArray;
});
}, [removeValue]);
return <div>{array.join(" ")}</div>;
};
솔루션 #2: 사용 조건부로 실행
가끔 당신은 그 일을 할 수 있는 조건을 찾을 수 있다.useEffect
예를 들어, 우리는 단지 우리의array
어레이에 다음이 포함된 경우removeValue
그러므로 우리는 일찍 돌아올 수 있다:
const MyComponent = ({ removeValue }) => {
const [array, setArray] = useState([1, 2, 3, 4, 5]);
// useEffect to remove `removeValue` from array
useEffect(() => {
const containsValue = array.includes(removeValue);
if (!containsValue) return;
const newArray = array.filter((value) => value !== removeValue);
setArray(newArray);
}, [array, removeValue]);
return <div>{array.join(" ")}</div>;
};
솔루션 #3: 사용useCompare
때로는 제약으로 인해 위의 해결책이 불가능할 때도 있으므로 여기에 좀 더 정교한 방법이 있다.이것은 더 복잡한 문제를 위한 것이지만, 나는 아직 그 문제를 보지 못했다.useEffect
두 그것은 내가 아래에 제공할 두 가지 추가 기능을 필요로 한다.
기능을 설명하기 위해 아래 코드에 대해 코멘트를 했다.
const MyComponent = ({ removeValue }) => {
const [array, setArray] = useState([1, 2, 3, 4, 5]);
// useCompare will return either `true` or `false` depending
// on if the value has changed. In this example, `useEffect` will
// rerun every time the array length changes.
// This can be applied to any other logic such as if removeValue
// changes, depending on when you want to run the `useEffect`
const arrayLengthChanged = useCompare(array.length);
useEffect(() => {
if (!arrayLengthChanged) return;
const newArray = array.filter((value) => value !== removeValue);
setArray(newArray);
}, [array, arrayLengthChanged, removeValue]);
return <div>{array.join(" ")}</div>;
};
솔루션 #4: 오류 비활성화(권장하지 않음)
마지막 '해결'은 문제를 회피하고 있다.경우에 따라서는 이 정도면 충분하고 완벽하게 작동하겠지만, 만약 이 경우 디버깅 문제가 발생할 수 있다.useEffect
나중에 변경하거나 올바르게 사용하지 않을 경우 변경하십시오.
이 예에서, 당신이 당신이 결코 당신이 그것을 실행할 필요가 없을 것이라는 것을 알고 있다고 말하시오.useEffect
어레이가 변경되면 종속 어레이에서 해당 어레이를 제거하고 다음 오류를 무시하십시오.
const MyComponent = ({ removeValue }) => {
const [array, setArray] = useState([1, 2, 3, 4, 5]);
// useEffect to remove `removeValue` from array
useEffect(() => {
const newArray = array.filter((value) => value !== removeValue);
setArray(newArray);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [removeValue]);
return <div>{array.join(" ")}</div>;
};
참조URL: https://stackoverflow.com/questions/58624200/react-hook-useeffect-has-a-missing-dependency-dispatch
'programing' 카테고리의 다른 글
과학적 표기법 없이 정밀한 방법으로 숫자 배열로 예쁘게 인쇄하는 방법? (0) | 2022.03.15 |
---|---|
각도 시도 캐치 vs 포수 오류 (0) | 2022.03.15 |
Ubuntu에서 pip을 통해 python3 버전의 패키지를 설치하는 방법? (0) | 2022.03.14 |
RouteEnter 후크 전에 vue-router 내의 다른 URL로 리디렉션하는 방법? (0) | 2022.03.14 |
계산된 속성에서 오류가 반환되는 이유 (0) | 2022.03.14 |