Revamp Your React Skills with These Must-Know Best Practices for Lightning-Fast Rendering!

Bestpractise May 14, 2023

At the beginning of react, there was a binding framework born that will get very handy features, that would it make easier to generate dynamic webpages. Let's say... react is awesome. Sure Agular also, but I think that react can be used for single components on existing webpage, to get modern, instead of using Angular, but anyway this is not part in this article.

Since react get very popular, you must consider to avoid missue of functions so I will tell you some good bestpractises for now

Use useState instead of variables

This is the one of the no brainer. Consider to use useState instead of variables. This makes you life more easier.

import AnotherComponent from 'components/AnotherComponent'

const Component = () => {
  // Don't do this.
  const value = { someKey: 'someValue' }

  return <AnotherComponent value={value} />
}

In the case above, AnotherComponent and everything that depends on value will rerender on every render, even if they are memoized with memo, useMemo or useCallback.

If you would add a useEffect to your component with value as a dependency, it would trigger on every render. The reason for that is that the JavaScript reference for value will be different on every render.

By using React's useState, React will keep the same reference for value all until you update it with setValue. React will then be able to detect when to and when not to trigger effects and recalculate memoizations.

import { useState } from 'react'
import AnotherComponent from 'components/AnotherComponent'

const Component = () => {
  // Do this instead.
  const [value, setValue] = useState({ someKey: 'someValue' })

  return <AnotherComponent value={value} />
}

Declare CSS outside components

I did it often to add some style into the components. That would look like this

import makeCss from 'some/css/in/js/library'

const Component = () => {
  // Don't do this.
  return <div className={makeCss({ background: red, width: 100% })} />
}

Instead of using inline css, just put it outside the componenten. The reason is, that the component must recreated everytime when it will be rendered. So instead take it out and make a reference:

import cssLibrary from 'some/css/in/js/library'

// Do this instead.
const someCssClass = makeCss({
  background: red,
  width: 100%
})

const Component = () => {
  return <div className={someCssClass} />
}

Use callback to prevent function recreations

Everytime when you use a function like "onclick" or s.th. else it will create a new function. To avoid this bunch of memory wasting, react provide useCallback. This will create one instance and keep the reference to this single instance then.

import { useCallback } from 'react'

const Component = () => {
  const [value, setValue] = useState(false)

  // This function will be recreated on each render.
  const handleClick = () => {
    setValue(true)
  }

  return <button onClick={handleClick}>Click me</button>
}

Now the change is very small

import { useCallback } from 'react'

const Component = () => {
  const [value, setValue] = useState(false)

  // This function will only be recreated when the variable value updates.
  const handleClick = useCallback(() => {
    setValue(true)
  }, [value])

  return <button onClick={handleClick}>Click me</button>
}

Maybe when it will get more complex, you can join him ;)

Do Not Use Hooks in If Statements

In React's Documentation it's well documented. One should never write conditional hooks, simply as that.

import { useState } from 'react'

const Component = ({ propValue }) => {
  if (!propValue) {
    // Don't do this.
    const [value, setValue] = useState(propValue)
  }

  return <div>{value}</div>
}

Use useReducer Instead of Multiple useState


You can avoid to bloat your code with multiple useStates that will get hard to maintain. It is more cumbersome to write, but you will prevent unnessesary renders and make it more readable and understandable.

I use a reducer when the useState is used more then three times in a code. So thie is a rule of thumb.

Here is a example of using only useState

import { useState } from 'react'

const Component = () => {
  // Do not add a lot of useState.
  const [text, setText] = useState(false)
  const [error, setError] = useState('')
  const [touched, setTouched] = useState(false)

  const handleChange = (event) => {
    const value = event.target.value
    setText(value)

    if (value.length < 6) {
      setError('Too short')
    } else {
      setError('')
    }
  }

  return <>
    {!touched && <div>Write something...</div> }
    <input type="text" value={text} onChange={handleChange} />
    <div>Error: {error}</div>
  </>
}

And now the corrected one

import { useReducers } from 'react'

const UPDATE_TEXT_ACTION = 'UPDATE_TEXT_ACTION'
const RESET_FORM = 'RESET_FORM'

const getInitialFormState = () => ({
  text: '',
  error: '',
  touched: false
})

const formReducer = (state, action) => {
  const { data, type } = action || {}

  switch (type) {
    case UPDATE_TEXT_ACTION:
      const text = data?.text ?? ''

      return {
        ...state,
        text: text,
        error: text.length < 6,
        touched: true
      }
    case RESET_FORM:
      return getInitialFormState()
    default:
      return state
  }
}

const Component = () => {
  const [state, dispatch] = useReducer(formReducer, getInitialFormState());
  const { text, error, touched } = state

  const handleChange = (event) => {
    const value = event.target.value
    dispatch({ type: UPDATE_TEXT_ACTION, text: value})
  }

  return <>
    {!touched && <div>Write something...</div> }
    <input type="text" value={text} onChange={handleChange} />
    <div>Error: {error}</div>
  </>
}

Use useRef Instead of useState When a Component Should Not Rerender

To avoid rendering you can useRef instead of useState. So vor example use this piece of code

import { useEffect } from 'react'

const Component = () => {
  const [triggered, setTriggered] = useState(false)

  useEffect(() => {
    if (!triggered) {
      setTriggered(true)

      // Some code to run here...
    }
  }, [triggered])
}

When you run the code above, the component will rerender when setTriggered is invoked. In this case, triggered state variable could be a way to make sure that the effect only runs one time (which actually doesn't work in React 18, learn why in this article about useRunOnce hook).

Since the only use of triggered variable in this case, is to keep track if a function has been triggered or not, we do not need the component to render any new state. We can therefore replace useState with useRef, which won't trigger the component to rerender when it is updated.

import { useRef } from 'react'

const Component = () => {
  // Do this instead.
  const triggeredRef = useRef(false)

  useEffect(() => {
    if (!triggeredRef.current) {
      triggeredRef.current = true

      // Some code to run here...
    }

  // Note missing dependency. This isn't optimal.
  }, [])
}

Conclusion

In conclusion, React is a powerful JavaScript library that offers many features to generate dynamic webpages. However, it's essential to follow best practices to ensure efficient rendering, improve code readability, and avoid unnecessary renders. By implementing tips such as using useState, useCallback, useReducer, and useRef, developers can write more maintainable code that delivers a seamless user experience. These practices are vital for anyone who wants to build high-performing, scalable web applications using React.


Did you like this article? Then maybe you can send me some Ko-fi

Also, you can subscribe to my free Newsletter!

Tags