0

I use React.memo to control re-render, but my component still re-rendered. my code like this:

in my Template Component:

const Template = (props: Props) => {
  const { propsValue, data } = props
  const [value, setValue] = useState(propsValue)

  const handleChange = (value) => {
    setValue(value)
    props.onChange(value)
  }
  
  return (
    <div>
      {info.type === 'input' && <Input value={value} onChange={(event, val) => handleChange(val) onBlur={(event) => handleBlur()} />
      {info.type === 'image' && <Uploader multiple value={value} uploadUrl={data.uploadUrl} onChange={(val) => handleChange(val)} />
      {info.type === 'select' && <Select onChange={(val) => handleChange(val)} />
    </div>
  )
}

const areEqual = (prevProps, nextProps) => {
  if (JSON.stringify(prevProps) !== JSON.stringify(nextProps)) {
    return false
  }
  return true
}

export default React.memo(EditTemplate, areEqual)

in my Uploader Component:

const Uploader = props => {
  const [value, setValue] = useState(props.value)
  let { uploadUrl, multiple } = props

  const handleChange = ({ file, fileList }) => {
    if (file.status === 'done') {
      setValue(fileList)
      props.onChange(fileList)
    } else {
      setValue(fileList)
    }
  }

  return (
     <div>
       <Upload fileList={value} multiple={multiple} action={uploadUrl} onChange={handleChange} />
     </div>
  )
}

const areEqual = (prevProps, nextProps) => {
  if (JSON.stringify(prevProps) !== JSON.stringify(nextProps)) {
    return false
  }
  return true
}

export default React.memo(Uploader, areEqual)

when I change value in Select Component, the areEqual seems like not work, the address of all images in Upload Component will reload. why...?

the performance like this:

enter image description here

how can I do?

serenas
  • 137
  • 4
  • 10
  • React [memo](https://reactjs.org/docs/react-api.html#reactmemo): This method only exists as a **performance optimization**. Do not rely on it to “prevent” a render, as this can lead to bugs. Straight from the docs. If `info.type` changes then `Uploader` is mounted/unmounted. Don't prematurely optimize. – Drew Reese Jul 21 '20 at 08:42
  • For those cases, you should consider using `useMemo` inside your component before re-rendering – Lhew Jul 21 '20 at 08:50

3 Answers3

2

The rerender might be because of the internal state change(setValue(value)). React.memo doesn't prevent rerender caused by a state change.

React.memo only checks for prop changes. If your function component wrapped in React.memo has a useState or useContext Hook in its implementation, it will still rerender when state or context change.

Docs

Ramesh Reddy
  • 10,159
  • 3
  • 17
  • 32
  • but why state change in select lead to uploader images reload...? can't understand... – serenas Jul 21 '20 at 08:51
  • Because of the reason stated in the answer. `Uploader` is a child of the `Template` component. The function assigned to select's `onChange` is changing the state which triggers the rerender. – Ramesh Reddy Jul 21 '20 at 08:54
  • I try to comment out `setValue(value)` in Template Component but images still reload. – serenas Jul 21 '20 at 09:04
  • 1
    What about `props.onChange(value)`? There are a few other things that can cause a rerender. Go through the docs. Memoize things that makes sense to be memoized using `useCallback`, `useMemo`. – Ramesh Reddy Jul 21 '20 at 10:16
0

You're passing the onChange param to Uploader like onChange={(val) => handleChange(val)} so this effectively creates new function on each render and probably React.memo gives false positive because of this. Also in Uploader you have props.onChange(fileList) and fileList might also be the reason if it's a different Array instance everytime.

In addition to @Ramesh Reddy's answer, you're calling setValue upon Select changes too, that could also be the reason, since you're using the new value as prop to Uploader.

If this is not the reason, you can add a codesandbox sample with reproduction of the issue.

Rosen Dimov
  • 1,055
  • 12
  • 32
0

thanks all very much~I find that the type of my value in Uploader is array, so when Select change ,props.onChange will be lead to change the value, so Uploader will reload.

I add useEffect in Uploader Component like this : useEffect(() => { setValue(formatValue(props.value)) }, [JSON.stringify(props.value)]) ,and then the images will not reload...

serenas
  • 137
  • 4
  • 10