1

I have a child filter input componenet inside kendo grid and my goa is stoping that components from rendering again and saving "input text" inside input field

   <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>}
    />

Now when I add some input inside that component, it passes value to the state called word and after 500 ms it triggers debounce and parses it to the redux state called "term"

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;

Now, whenever redux state changes, it triggers useEffect inside of a kendo grid and gets new data from 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]);

So what's the problem? SearchInput Componenet rerenders even if its inside React.memo() and from the profiler I am getting that SearchInput rendered because "hook change".

I am totally stuck, I have no idea how to procced.

Mladen Milosavljevic
  • 1,720
  • 1
  • 12
  • 23
  • 1
    it does render on every change because of setWord(e.target.value), but prob you mean extra renders? how many times it renders for each letter you type in the input? – Jelte Homminga Jun 09 '21 at 19:31
  • 1
    if I look at the example in the docs from kendo grid they pass the component name to filterCell, while you pass a function that return a component to filterCell. It depends on the implementation of GridColumn if this matter. You could change this to filterCell={SearchInput} to check it this gets rid of the rerender – Jelte Homminga Jun 09 '21 at 19:34
  • hey jelte, for filter cell to inheirt props you need to pass a function with props argument but yeah that was the issue. Thank you. – Mladen Milosavljevic Jun 09 '21 at 20:42

1 Answers1

1

You needlessly set local state with const [word, setWord] = useState('');, setWord will re render your component because local state changed. You can make the input an uncontrolled component

Here is an example of what you can do:

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>
HMR
  • 37,593
  • 24
  • 91
  • 160
  • Thanks for this good answer, but actual answer was mistake with hacking up kendo componenets. I passed function into filter cell instead of just componenet. Your input was much valued, and i corrected mistakes that you pointed out here with extra state etc. ty – Mladen Milosavljevic Jun 09 '21 at 20:43