Как прокомментировано; Я думаю, что ваш реальный код, который бесконечные циклы, зависит от setAnswers
. В вашем вопросе вы забыли добавить эту зависимость, но код ниже показывает, как вы можете предотвратить изменение setAnswers
и вызвать бесконечное l oop:
const GOT_DATA = 'GOT_DATA';
const reducer = (state, action) => {
const { type, payload } = action;
console.log('in reducer', type, payload);
if (type === GOT_DATA) {
return { ...state, data: payload };
}
return state;
};
//I guess you imported this and this won't change so
// useCallback doesn't see it as a dependency
const getAnswersData = id => ({
type: GOT_DATA,
payload: id,
});
const useAnswersDispatch = dispatch => {
// const dispatch = useDispatch(); //react-redux useDispatch will never change
//never re create setAnswers because it causes the
// effect to run again since it is a dependency of your effect
const setAnswers = React.useCallback(
questionID => dispatch(getAnswersData(questionID)),
//your linter may complain because it doesn't know
// useDispatch always returns the same dispatch function
[dispatch]
);
return {
setAnswers,
};
};
const Data = ({ id }) => {
//fake redux
const [state, dispatch] = React.useReducer(reducer, {
data: [],
});
const { setAnswers } = useAnswersDispatch(dispatch);
React.useEffect(() => {
setAnswers(id);
}, [id, setAnswers]);
return {JSON.stringify(state.data)}
; }; const App = () => {const [id, setId] = React.useState (88); return (
setId (id => id + 1)}> увеличить id
); }; ReactDOM.render ( , 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>
Вот ваш исходный код, вызывающий бесконечное l oop, потому что setAnswers постоянно меняется.
const GOT_DATA = 'GOT_DATA';
const reducer = (state, action) => {
const { type, payload } = action;
console.log('in reducer', type, payload);
if (type === GOT_DATA) {
return { ...state, data: payload };
}
return state;
};
//I guess you imported this and this won't change so
// useCallback doesn't see it as a dependency
const getAnswersData = id => ({
type: GOT_DATA,
payload: id,
});
const useAnswersDispatch = dispatch => {
return {
//re creating setAnswers, calling this will cause
// state.data to be set causing Data to re render
// and because setAnser has changed it'll cause the
// effect to re run and setAnswers to be called ...
setAnswers: questionID =>
dispatch(getAnswersData(questionID)),
};
};
let timesRedered = 0;
const Data = ({ id }) => {
//fake redux
const [state, dispatch] = React.useReducer(reducer, {
data: [],
});
//securit to prevent infinite loop
timesRedered++;
if (timesRedered > 20) {
throw new Error('infinite loop');
}
const { setAnswers } = useAnswersDispatch(dispatch);
React.useEffect(() => {
setAnswers(id);
}, [id, setAnswers]);
return {JSON.stringify(state.data)}
; }; const App = () => {const [id, setId] = React.useState (88); return (
setId (id => id + 1)}> увеличить id
); }; ReactDOM.render ( , 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>