Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it safe to useMemo for JSX?

I'm trying to do a memoize Modal and I have a problem here.

When I change input I dont need to re-render the Modal component.

For example:

Modal.tsx looks like this:

import React from "react";
import { StyledModalContent, StyledModalWrapper, AbsoluteCenter } from "../../css";

interface ModalProps {
  open: boolean;
  onClose: () => void;
  children: React.ReactNode
};

const ModalView: React.FC<ModalProps> = ({ open, onClose, children }) => {
  console.log("modal rendered");
  
  return (
    <StyledModalWrapper style={{ textAlign: "center", display: open ? "block" : "none" }}>
      <AbsoluteCenter>
        <StyledModalContent>
          <button
            style={{
              position: "absolute",
              cursor: "pointer",
              top: -10,
              right: -10,
              width: 40,
              height: 40,
              border: 'none',
              boxShadow: '0 10px 10px 0 rgba(0, 0, 0, 0.07)',
              backgroundColor: '#ffffff',
              borderRadius: 20,
              color: '#ba3c4d',
              fontSize: 18
            }}
            onClick={onClose}
          >
            X
          </button>
          {open && children}
        </StyledModalContent>
      </AbsoluteCenter>
    </StyledModalWrapper>
  );
};

export default React.memo(ModalView);

Here is an example of how I wrap it.

import React from 'react'
import Modal from './modal';

const App: React.FC<any> = (props: any) => {
  const [test, setTest] = React.useState("");
  const [openCreateChannelDialog, setOpenCreateChannelDialog] = React.useState(false);
    
  const hideCreateModalDialog = React.useCallback(() => {
    setOpenCreateChannelDialog(false);
  }, []);

  return (
    <>
      <input type="text" value={test} onChange={(e) => setTest(e.target.value)} />
      <button onClick={() => setOpenCreateChannelDialog(true)}>Create channel</button>
    
      <Modal
        open={openCreateChannelDialog}
        onClose={hideCreateModalDialog}
        children={<CreateChannel onClose={hideCreateModalDialog} />}
      />
    </>
};

I know, Modal re-rendered because children reference created every time when App component re-renders (when I change an input text).

Know I'm interested, if I wrap <CreateChannel onClose={hideCreateModalDialog} /> inside React.useMemo() hook

For example:

  const MemoizedCreateChannel = React.useMemo(() => {
    return <CreateChannel onClose={hideCreateModalDialog} />
  }, [hideCreateModalDialog]);

And change children props inside Modal

from:

children={<CreateChannel onClose={hideCreateModalDialog} />}

to

children={MemoizedCreateChannel}

It works fine, but is it safe? And it is only one solution that tried to memoize a Modal?

like image 722
Tazo Leladze Avatar asked Feb 28 '20 14:02

Tazo Leladze


People also ask

Can useMemo return JSX?

We can also wrap the return value of our functional component in a useMemo callback to memoize, the component would re-render but the return value will be based on its dependencies, if changed will return a new value, if not will return cached version. The return value is a JSX that React uses to create a virt.

Should you really use useMemo in React?

According to React documentation, useMemo should be used to optimize performance, not to store data. In the future, React may choose to “forget” some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components.

In which situation would you use useMemo () in React?

React has a built-in hook called useMemo that allows you to memoize expensive functions so that you can avoid calling them on every render. You simple pass in a function and an array of inputs and useMemo will only recompute the memoized value when one of the inputs has changed.

When should we not use usememo in JavaScript?

We should also not use useMemo when the function returns a primitive value, such as a boolean or a string. Because primitive values are passed by value, not by reference, it means that they always remain the same, even if the component is re-rendered.

What is the use of usememo in react?

UseMemo is one of the hooks offered by React. This hook allows developers to cache the value of a variable along with a dependency list. If any variables in this dependency list changes, React will re-run the processing for this data and re-cache it.

When does usememo do more harm than good?

Another and the most obvious situation in which useMemo probably does more harm than good is when the function being memoized does not perform expensive operations. useMemo itself requires memory so if we are trying to over-optimize by memoizing every function, it might slow the application down.

Is the usememo hook hurting your application?

If you’ve used Hooks in any serious production app, then you’ve likely been tempted to use the useMemo Hook in one of these two categories. I’ll show you why these are unimportant and likely hurting the performance of your application, and more interestingly, I’ll show you my recommendations on how not to overuse useMemo in these use cases.


1 Answers

Memoizing JSX expressions is part of the official useMemo API:

const Parent = ({ a }) => useMemo(() => <Child1 a={a} />, [a]); 
// This is perfectly fine; Child re-renders only, if `a` changes

useMemo memoizes individual children and computed values, given any dependencies. You can think of memo as a shortcut of useMemo for the whole component, that compares all props.

But memo has one flaw - it doesn't work with children:

const Modal = React.memo(ModalView);

// React.memo won't prevent any re-renders here
<Modal>
  <CreateChannel />
</Modal>

children are part of the props. And React.createElement always creates a new immutable object reference (REPL). So each time memo compares props, it will determine that children reference has changed, if not a primitive.

To prevent this, you can either use useMemo in parent App to memoize children (which you already did). Or define a custom comparison function for memo, so Modal component now becomes responsible for performance optimization itself. react-fast-compare is a handy library to avoid boiler plate for areEqual.

like image 194
ford04 Avatar answered Oct 17 '22 11:10

ford04