pmndrs / leva

🌋 React-first components GUI
https://leva.pmnd.rs
MIT License
4.78k stars 195 forks source link

In the first example, the buttons are dynamically created from an array. #503

Open RonBonBon opened 1 month ago

RonBonBon commented 1 month ago

In the first example, the buttons are dynamically created from an array. Every time a button is pressed, it changes some local state, which causes a re-render and that causes the buttons to be re-created. See console output in example link, CodeSandbox : https://codesandbox.io/s/leva-test-1-c1omd2?file=/src/App.jsx

This is pretty obvious to the expert eye why the buttons are being recreated when the state changes.

So, if I try to memoize the creation of the buttons. it works as intended and doesn't recreate the buttons every time the local state changes.

  const buttons = {}
  useMemo(() => {
    positions.forEach((p, i) => {
      console.log('creating button ' + i)
      buttons['button ' + i] = button(() => {
        setTo(p)
      })
    })
  }, [positions])

  useControls('Camera', buttons)

However, there is now the react warning "react-hooks/exhaustive-deps", despite my example now working as intended. See console output in example 2 link : https://codesandbox.io/s/leva-test-2-mj1d09?file=/src/App.jsx In the example 2 link, the buttons are being created only once, even when you change the local state. Perfect, except for the warnings.

So then to solve the react-hooks/exhaustive-deps warning, I add the extra dependency to the useMemo hook.

  const buttons = {}
  useMemo(() => {
    positions.forEach((p, i) => {
      console.log('creating button ' + i)
      buttons['button ' + i] = button(() => {
        setTo(p)
      })
    })
  }, [positions, buttons])

And then I get a different react-hooks/exhaustive-deps warning saying to wrap the initialization of 'buttons' in its own useMemo() Hook. CodeSandbox Example 3 : https://codesandbox.io/s/leva-test-3-xyjcjc?file=/src/App.jsx

Ok, so then I do it,

  const buttons = useMemo(() => {}, [])
  useMemo(() => {
    positions.forEach((p, i) => {
      console.log('creating button ' + i)
      buttons['button ' + i] = button(() => {
        setTo(p)
      })
    })
  }, [positions, buttons])

Codesandbox : https://codesandbox.io/s/leva-test-4-lmhpey?file=/src/App.jsx

So, all warnings are now gone, but if I run it, then I get TypeError Cannot set properties of undefined (setting 'button 0')

I have tried all kinds of combinations to try and get this small demo to not recreate the buttons each time the state changes while also not having the react-hooks/exhaustive-deps warnings.

I'm sure the answer is there, but I'm just unable to see it.

Originally posted by @Sean-Bradley in https://github.com/pmndrs/leva/discussions/393

Sean-Bradley commented 1 month ago

It seems you have created a new issue from an issue that was already solved and closed. https://github.com/pmndrs/leva/discussions/393#discussioncomment-3881633

Your problem is unclear because this is a verbatim copy of just one of the comments from the original thread.