0

I have trouble to avoid re-renders. Have a look in my simplified example: https://codesandbox.io/s/gracious-satoshi-b3p1u?file=/src/App.js Open the console. I have expensive and heavy code in the child hook which filters a big array (not in this lightweight example). Every time "child has been rendered" get's printed into the console, my browser laggs and crashes if i do it too often.

There is no state in Child. I just use it to add a certain text piece (user can select from a big list of emojis and special chars) if i click to the textarea which needs a callback to communicate. I tried React.useMemo() and useMemo() but neither worked.

2 Answers 2

1

Try this. Add useMemo dependancies as needed,

export default function App() {
  const [data, setData] = useState({ title: "default title", test: null });

  const childs = useMemo(() => {
    return (
      <Child clickCallback={(x) => setData({ ...data, test: x })} />
    )
  }, []);

  return (
    <div>
      <p>Title: {data.title}</p>
      <p>Test: {data.test}</p>

      <input onChange={(x) => setData({ ...data, title: x.target.value })} />
      {childs}
    </div>
  );
}

Sign up to request clarification or add additional context in comments.

5 Comments

Great! This looks like less effort compared to the solution of @salamaashoush . Is there also a way to concat with setData({ ...data, test: data.test + x }) inside useMemo() if i want to append instead of replace?
Not sure what is mean by 'concat'
Sorry, i mistyped. I want to append "Text 2" to the data (for example "Test 1"). Let's take this as an example: I click on Test 1 and then on Test 2. "test" should be "Test 1 Test 2".
export default function App() { const [data, setData] = useState({ title: "default title", test: null }); const setMyData = useCallback(title => { const preVal = data.test; setData({...data, test: ${preVal} ${title}}); }, [setData, data]); return ( <div> <p>Title: {data.title}</p> <p>Test: {data.test}</p> <input onChange={(x) => setData({ ...data, title: x.target.value })} /> <Child clickCallback={(x) => setMyData(x)} /> </div> ); }
Works, but this seems to re-render the child: Codesandbox . This will cause a lag.
0

You should use useCallback to memoize functions/event handlers and avoid creating them every time, and also for child components you should use memo to rerender only if props have changed

App.js

import React, { useCallback, useState } from "react";
import Child from "./Child";

export default function App() {
  const [data, setData] = useState({ title: "default title", test: null });
  const cb1 = useCallback(
    (x) => setData({ ...data, title: x.target.value }),
    []
  );
  const cb2 = useCallback((x) => setData({ ...data, test: x }), []);

  return (
    <div>
      <p>Title: {data.title}</p>
      <p>Test: {data.test}</p>

      <input onChange={cb1} />
      <Child clickCallback={cb2} />
    </div>
  );
}

Child.js

import { memo } from "react";

export default memo(function Child(props) {
  return (
    <>
      {console.log("child has been rendered")}
      <button onClick={() => props.clickCallback("test")}>test</button>
      <button onClick={() => props.clickCallback("test2")}>test2</button>
      <button onClick={() => props.clickCallback("test3")}>test3</button>
      <button onClick={() => props.clickCallback("test4")}>test4</button>
    </>
  );
});

check a working example here https://codesandbox.io/s/vibrant-curran-tv502

2 Comments

This works great. But can i also append the callback to the existing data instead of "replacing" it? I want to keep the previous content and apply the new one to the back of it.
I don't get it. This does not re-render but neither appends. codesandbox.io/s/sad-lederberg-se6w1?file=/src/App.js

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.