henry sneed
henry sneed

Reputation: 526

Comparing different approaches of setting initial state

When initializing state in React, is it bad practice to define functions inside of useState rather than using useEffect? Or is it best to use useMemo?

My understanding is that variables inside useState will be set on the first render of the component. Are there any additional considerations when using functions to set the initial state?

Function

import React, { useState, useEffect } from "react";

export default function Token() {
  const a = 5
  const b = 5

  const findToken = (a,b) => {
    return a*b;
  };

  const [token, setToken] = useState(() => {
    return findToken(a,b);
  });

  return (
    <div>
      <h1>Token: {token}</h1>
    </div>
  );
}

useEffect

import React, { useState, useEffect } from "react";

export default function Token() {
  const a = 5
  const b = 5
  const [token, setToken] = useState();
   
  useEffect(() => {
    setToken(a*b);
  }, []);

  return (
    <div>
      <h1>Token: {token}</h1>
    </div>
  );
}

UseMemo

import React, { useState, useEffect } from "react";

export default function Token() { 
  const a = 5
  const b = 5

  const findToken = (a,b) => {
    return a*b;
  };

  const [token, setToken] = useState(useMemo(() => findToken(a,b), [a,b]));

  return (
    <div>
      <h1>Token: {token}</h1>
    </div>
  );
}

Edit interesting-forest-bs2hvy

Upvotes: 1

Views: 764

Answers (2)

Giorgi Moniava
Giorgi Moniava

Reputation: 28654

Below I will try to compare some of the approaches you listed for setting initial state.

useState with initializer function

const [token, setToken] = useState(() => {
    return findToken(a,b);
});

You use this approach when findToken is relatively expensive, and using this approach it will be guaranteed that findToken will be executed only once. From the docs:

If the initial state is the result of an expensive computation, you may provide a function instead, which will be executed only on the initial render

Because if you passed an expression instead of function to useState, that expression would be run on each render, just its value gets ignored after first render.

Approach with useEffect

  useEffect(() => {
    setToken(a*b);
  }, []);

In terms of functionality this is similar to approach with useState above, but this is unnecessary and causes an additional re render.

Also there is another difference, the initializer function passed to useState should be pure, whereas you don't have that requirement with useEffect, so in the approach with useEffect you have advantage that you can set value in state which is result of say an API call.

Approach with useMemo

  const [token, setToken] = useState(useMemo(() => findToken(a,b), [a,b]));

Above approach doesn't make sense actually because what will happen is first time useMemo runs, its result will be stored in useState. Then, all other values useMemo will generate will be actually ignored - as already mentioned in the first section about passing expressions to useState.

Upvotes: 3

CertainPerformance
CertainPerformance

Reputation: 370689

If the value will only be set exactly once, and the operation that needs to be performed isn't expensive, then it would make the most sense to avoid state and use only useMemo. For example, code like this:

const [token, setToken] = useState(() => {
    return findToken(a,b);
});

if setToken isn't called elsewhere and the calculation isn't expensive, would always be better refactored to

const token = useMemo(() => findToken(a, b), []);

If the value could be set multiple times depending on logic in your app, state will be needed.

If the operation that calculates the value is expensive enough to cause noticeable delays when rendering, then you might decide to compensate for that by using state, initially render an empty value, and then use useEffect to calculate (and set the state) for the expensive value after the component has been painted to the screen.

If you set state in an effect hook (as opposed to useMemo), keep in mind that that'll cause re-rendering of components lower in the tree, which is sometimes undesirable.

Upvotes: 2

Related Questions