当useselector和usedispatch同时使用时,造成无线环路

时间:2020-12-28 01:48:01

标签: redux

本来想用hook来代替麻烦的connect,但是导致了一个莫名其妙的问题,但是,当同时使用use selector和使用dispatch时,我先在modal中触发了效果,会导致死循环 。

有我的钩子和模态?文件名:dict.js

import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
/**

 * A Hook for get dict name
 * @param codes List
   */
   export function useDict(codes) {
   const dispatch = useDispatch();
   useEffect(() => {
   if (codes && dispatch) {
     dispatch({
       type: 'dict/getDict',
       payload: {
         codes
       },
     });
   }
   }, [dispatch, codes]);
   // select state
   const dict = useSelector(({ dict }) => dict);
   return dict.dictList;
   }

//  mock getDictData
const getDictData = () => new Promise((success, error) => {
  setTimeout(() => {
    success({ name: Math.floor(Math.random() * 10) });
  }, 100);
});
export default {
  namespace: 'dict',
  state: {
    dictList: [],
  },
  reducers: {
    loopData(state, action) {
      console.log('loopData', action.list);
      return { ...state, dictList: action.list };
    },
  },
  effects: {
    * getDict({ payload }, { call, put }) {
      const { codes } = payload;
      const response = yield call(getDictData, codes);
      yield put({
        type: 'loopData',
        list: response,
      });
        },
    },
};

我的应用索引页面?文件名:index.js

import { useDict } from './dict.js';
const Index = () => {
const dictlist = useDict(['hello'])
 return <h1>Hello !!!</h1>
}
export default Index;

1 个答案:

答案 0 :(得分:0)

问题出在这一行:const dictlist = useDict(['hello']) 因为 ['hello'] 是每次渲染时的新引用,它会导致效果在 useDict 中运行,从而导致组件重新渲染并且您有一个无限循环。

由于不清楚 ['hello'] 的来源,所以我添加了一个示例,如何防止重新创建它,除非需要使用 React.useMemo 重新创建它。

const { Provider, useDispatch, useSelector } = ReactRedux;
const { createStore, applyMiddleware, compose } = Redux;
const { useEffect, useState, useMemo } = React;

function useDict(codes) {
  const dispatch = useDispatch();
  useEffect(() => {
    if (codes && dispatch) {
      dispatch({
        type: 'dict/getDict',
        payload: {
          codes,
        },
      });
    }
  }, [dispatch, codes]);
  // select state
  const dict = useSelector(({ dict }) => dict);
  return dict.dictList;
}

const initialState = {
  dict: [],
};
const reducer = (state, { type, payload }) => {
  if (type === 'dict/getDict') {
    const { codes } = payload;
    return {
      ...state,
      dict: { dictList: codes[0].split('') },
    };
  }
  return state;
};
//creating store with redux dev tools
const composeEnhancers =
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
  reducer,
  initialState,
  composeEnhancers(
    applyMiddleware(() => (next) => (action) =>
      next(action)
    )
  )
);
const App = () => {
  const [txt, setTxt] = useState('ABC');
  //only re create dictArg if txt changed
  const dictArg = useMemo(() => [txt], [txt]);
  const dict = useDict(dictArg);
  return (
    <div>
      <input
        type="text"
        value={txt}
        onChange={(e) => setTxt(e.target.value)}
      />
      dict:<pre>{JSON.stringify(dict, undefined, 2)}</pre>
    </div>
  );
};

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  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>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js"></script>
<div id="root"></div>