Quick React Mistakes

Posted on October 2, 2021
Tags: javascript

1 Things NOT to do

const bleh = useRef(..)
useEffect(()=>{..},[bleh.current]);

2 Referential equality

function Foo({bar, baz}) {
  const options = {bar, baz}
  React.useEffect(() => {
    buzz(options)
  }, [options]) // we want this to re-run if bar or baz change
  return <div>foobar</div>
}

function Blub() {
  return <Foo bar="bar value" baz={3} />
}

useEffect is worthless in the above scenario since js will say each new rerender of options is “different”

function Foo({bar, baz}) {
  React.useEffect(() => {
    const options = {bar, baz}
    buzz(options)
  }, [bar, baz]) // we want this to re-run if bar or baz change
  return <div>foobar</div>
}

The above works but ONLY IF bar and baz are primitive types. If we defined bar and baz as a function and list type, the above would fail.

function Foo({bar, baz}) {
  React.useEffect(() => {
    const options = {bar, baz}
    buzz(options)
  }, [bar, baz])
  return <div>foobar</div>
}

function Blub() {
  const bar = React.useCallback(() => {}, [])
  const baz = React.useMemo(() => [1, 2, 3], [])
  return <Foo bar={bar} baz={baz} />
}

The above now works.

3 Objects and useEffect, referential equality

https://dev.to/vicnovais/understanding-referential-equality-in-reacts-useeffect-2m7o

3.1 useRef and useEffect chaos

  • const myref = useRef() creates an object myref
    • myref is an object with a property called current
    • even if myref.current changes, aka the DOMElement changes, myref the object itself maintains the same reference since only the property current changed.
  • useEffect only detects referential equality.

For those reasons above
useEffect(()=>{},[myref]) doesnt work
but… what if we did useEffect(()=>{},[myref.current])?
We naively expect it to work since myref.current does change reference BUT REMEMBER even if myref.current changes meaning the DOM changed, it does not trigger a react rerender.
useEffect only works when a rerender is triggered, meaning it will never trigger.

Example is D3 controlling the DOM, D3 does not trigger rerenders therefore useEffect on D3 controlled elements wont work!!

4 Nested component definition

//DO NOT DO THIS
const OuterComponent = () => {
  const [getstate,setstate] = useState();
  const InnerComponent = () => {
    return(
      <input type="text" onChange={(e)=>setstate(..)}>
    )
  }
  return(
    <>
      <InnerComponent/>
    </>
  )
}
//DO THIS
const InnerComponent = ({setstate}) => {
  return(
    <input type="text" onChange={(e)=>setstate(..)}>
  )
}

const OuterComponent = () => {
  const [getstate,setstate] = useState();
  return(
    <>
      <InnerComponent setstate={setstate}/>
    </>
  )
}

WHY: Whenever OuterComponent rerenders, it redeclares the InnerComponent. The InnerComponent of the snd render is different from the InnerComponent from the first render so you lose the text and lose focus on the textbox.

This is what the react offical docs talk about “lifting state up to the parent component”.
WARN: Do not mistake “lifting state up” to mean nesting definitions.

https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unstable-nested-components.md