React.MEMO() 无法在 KendoReact 中使用其内部的钩子(可能是 useDispatch)

React.MEMO() fails to work with hooks inside of it (probably useDispatch) within KendoReact

我在 kendo grid 中有一个子过滤器输入组件,我的目标是阻止该组件再次渲染并在输入字段中保存“输入文本”

   <GridColumn
      field="name"
      title="Document Name"
      headerCell={HeaderCell}
      className="tableCell"
      cell={LinkCell}
      filterCell={() =>  <SearchInput className={filtersToggler} /> }
      footerCell={(props) => <FooterCell {...props} colSpan={4} total={total}></FooterCell>}
    />

现在,当我在该组件中添加一些输入时,它会将值传递给名为 word 的状态,并在 500 毫秒后触发去抖并将其解析为名为“term”的 redux 状态“

const SearchInput = React.memo(() => {
  const [word, setWord] = useState('');

  const dispatch = useDispatch();

  const deb = useCallback(
    debounce((text) => dispatch({ type: 'SET_TERM', term: text }), 1000),
    []
  );

  const handleText = (text) => {
    deb(text);
  };
  return (
    <input
      className="searchInput"
      value={word}
      type="search"
      placeholder="Search.."
      onChange={(e) => {
        handleText(e.target.value)
        setWord(e.target.value);
      }}></input>
  );
});

export default SearchInput;

现在,每当 redux 状态发生变化时,它都会在 kendo 网格内触发 useEffect 并从 API.

获取新数据
  const searchWord = useSelector((state) => state.search.term);
  const classifications = useSelector((state) => state.search.classifications);
  const date = useSelector((state) => state.search.date);

  useEffect(() => {
    const data = searchDocsByName(searchWord, date, classifications);
    data.then((i) => {
      setDocuments(i.data.data);
      setTotal(i.data.data.length);
    });
  }, [searchWord, date, classifications]);

所以问题是什么? SearchInput Componenet 重新呈现,即使它在 React.memo() 内部,并且从分析器中我得到了 SearchInput 呈现,因为“挂钩更改”。

我完全卡住了,我不知道如何处理。

您不必要地使用 const [word, setWord] = useState(''); 设置本地状态,setWord 将重新呈现您的组件,因为本地状态已更改。您可以使输入成为 uncontrolled component

这是您可以执行的操作的示例:

const { Provider, useDispatch } = ReactRedux;
const { createStore, applyMiddleware, compose } = Redux;
const { useCallback } = React;

const initialState = {};
const reducer = (state, { type, term }) => {
  console.log('in reducer', type, term);
  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 debounce = (fn, time) => {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), time);
  };
};
const SearchInput = React.memo(function SearchInput() {
  console.log('rendering SearchInput');
  const dispatch = useDispatch();
  const deb = useCallback(
    debounce(
      (text) => dispatch({ type: 'SET_TERM', term: text }),
      1000
    ),
    []
  );
  return (
    <input
      className="searchInput"
      type="search"
      placeholder="Search.."
      onChange={(e) => {
        deb(e.target.value);
      }}
    ></input>
  );
});

const App = () => {
  return <SearchInput />;
};

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>