Hi,
One of my grid columns uses a child component to display its own data for each row in the grid. I have a demo here:
https://stackblitz.com/edit/react-mu6rlr?file=app/main.jsx
The issue is that any state change in the parent causes that child in the grid column to unmount re-mount. Try opening the console at the bottom right. Then type any text in the input. You can see that for each state change, the child unmounts and re-mounts.
In my real app, the child is fetching data with axios. So for each keystroke (or ANY state change), that child unmounts, remounts, and fetches the data again. This also happens with any action on the grid, such as sorting. Sort the grid, and the child unmounts and remounts.
How can I prevent this behavior? I can't have the child re-fetching the data like that.
Thanks for any help.
5 Answers, 1 is accepted
Hello, Grid,
Thank you for the example.
This occurs because a new function is made inside render each time the state is changed.
This can be resolved by utilizing the useMemo hook which will memorize the cell and not re-mount it every time:
https://stackblitz.com/edit/react-mu6rlr-cc837t?file=app/main.jsx
Also, if the props from the parent component have to be passed to the custom cell, we recommend using the new context API. This is an example with context:
https://stackblitz.com/edit/react-mu6rlr-ei1u9a?file=app/main.jsx
I hope one of this approaches proves helpful.
Regards,
Stefan
Progress Telerik
Is this approach still valid?
According to this eslint rule https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-unstable-nested-components.md and this SonarLint rule https://rules.sonarsource.com/javascript/RSPEC-6478 it seems that useCallback shouldn't be used. I'm still getting flagged on both rules when utilizing useMemo.
The recommended approach is to avoid inline functions and only pass the name of the custom component to the "cell" property. If additional properties or references from the main component have to be passed to the custom cell you will have to use React Context:
<Column field="" title="Qty" cell={Child} />
And here is once again an example with React Context and custom cell:
For all those wondering how to stop the re-render "unfocusing" in other kendo components that you are making controlled components (see Forms, Conversational UI, etc) useMemo worked!
Here is an example (@stefan you might want to add this to the docs for Forms and Conversational UI under the "controlled section" to help out future devs) -- this also solves the "multi line message box" but you have to wire up the send button action yourself.
```
import React, { SyntheticEvent, useMemo, useState } from 'react';
import { Button } from '@progress/kendo-react-buttons';
import { Chat, ChatMessageBoxProps } from '@progress/kendo-react-conversational-ui';
import { Editor } from '@progress/kendo-react-editor';
import { useDebouncedCallback } from 'use-debounce/lib';
import { getRawText } from '../../util/HtmlToText';
export interface MessageBoxProps extends ChatMessageBoxProps {
onChange: Function;
onSend: Function;
value?: string;
}
const MessageBox = ({ onSend, toolbarButton, onChange, value }: MessageBoxProps) => {
// Debounce callback
const [debouncedCallback] = useDebouncedCallback(onChange, 500); // helps reduce re-renders
return (
<>
{toolbarButton}
<Editor
defaultEditMode="div"
style={{ width: 'calc(100% - 100px)' }}
contentStyle={{ width: '100%' }}
onChange={e => debouncedCallback(getRawText(e.html))}
defaultContent={value}
/>
<Button onClick={onSend}>
Send
</Button>
</>
);
}
export type ChatWithEditorProps = {
onChange: (values: { [name: string]: any; }, event?: SyntheticEvent<any, Event> | undefined) => void;
data: any;
messages: any[];
};
const ChatWithEditor = ({ onChange, data, messages }: ChatWithEditorProps) => {
const [showToolbar, setShowToolbar] = useState(false);
const messageBox = useMemo(
() => (props: any) => <MessageBox {...props} onChange={text => onChange({ ...data, text })} value={data.text} />,
[]
);
return (
<Chat
width="100%"
user={{ id: data.from }}
messages={messages}
onToolbarActionExecute={() => setShowToolbar(!showToolbar)}
showToolbar={showToolbar}
placeholder="Send a message"
toolbar={
<span>
<Button icon="image" look="outline" onClick={() => console.log('toolbar btn clicked')} />
</span>
}
messageBox={messageBox}
/>
);
};
export default ChatWithEditor;
```
we lost all formatting on that paste, here is a stackblitz
https://stackblitz.com/edit/react-mu6rlr-b3a9vn?file=app%2Fmain.tsx
it doesn't "work" in stack blitz, only there for formatting purposes so you can see what I did. (all in typescript, that's why it didn't work ootb in stackblitz, but I didn't spend the time to fix it)
Hello, Kyle,
Thank you very much for sharing the example, it is highly appreciated.
I have also added some Telerik points to your account for sharing this with the KendoReact community.
Regards,
Stefan
Progress Telerik
Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.