React Hook useEffect缺少依赖项:“ dispatch”

时间:2019-10-30 11:17:32

标签: reactjs

这是我第一次使用react 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);

2 个答案:

答案 0 :(得分:3)

dispatch来自自定义hook,因此它没有稳定的签名,因此将更改每个渲染。通过将处理程序包装在useCallback钩子内,添加一个附加的依赖层

   const [foo, dispatch] = myCustomHook()

   const stableDispatch = useCallback(dispatch, []) //assuming that it doesn't need to change

   useEffect(() =>{
        stableDispatch(foo)
   },[stableDispatch])

答案 1 :(得分:1)

我认为您可以从根本上解决问题,但这意味着更改useCombinedReducers,我分叉了存储库并创建了a pull request,因为我不认为useCombinedReducers每次调用时都应该返回新的引用以进行分发。

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>