DEV Community

Cover image for React - You might not need that many states!
Bruno Noriller
Bruno Noriller

Posted on • Originally published at linkedin.com

React - You might not need that many states!

You probably know not to use one single state for everything a component needs.
But in avoiding that, sometimes you feel compelled to split the state in one for each thing you need.

But you don’t need to do that!

If you have things that change together, then you end up setting a “waterfall“ of events that could be bundled together.

The form example:

Let’s say you have this form example:

function FormExample(){
    ???

    return (
        <form>
            <input name="data1" />
            <input name="data2" />
            <input name="data3" />
        </form>
    )
}
Enter fullscreen mode Exit fullscreen mode

You can have it split into three states:

const [data1, setData1] = useState(data1Default)
const [data2, setData2] = useState(data2Default)
const [data2, setData3] = useState(data3Default)
Enter fullscreen mode Exit fullscreen mode

But you could also:

const [allData, setAllData] = useState(dataDefault)
// and then...
const updateData = (event) => {
    setAllData((oldState) => ({
        ...oldState, 
        [event.target.name]: event.target.value
    }))
}
Enter fullscreen mode Exit fullscreen mode

With this, as long as you name the inputs you can update all the state in one go.

And it doesn’t need to be a form, anywhere the data will always change together is a place you might not need to split.

And if one piece depends on another one, you would have a harder time dealing with it or having useEffects using the values as dependencies or risk having outdated data showing.

TIL: this works with nested data too!

The dependency array of hooks works with nested data [data.like.this] and not only that… even if you have data that might not be there it works!
So, you can have the state in one place and use [data?.optional?.chaining] instead of splitting or putting the entire object in the array with a if value then do stuff.


TLDR: Split if it makes sense for the data to be in different places, else, as long as it’s readable and maintainable… you might want to consider sticking to one state.


Cover Photo by Kelly Sikkema on Unsplash

Discussion (12)

Collapse
brense profile image
Rense Bakker

Or for forms, if you're just submitting the values and not displaying them anywhere else, you can use uncontrolled inputs! :D

function MyComponent(){
  const handleSubmit = useCallback((evt) => {
    evt.preventDefault()
    console.log(evt.target.elements.data1.value) // etc...
    // submit your data...
  }, [])

  return <form onSubmit="handleSubmit">
    <input name="data1" />
    <input name="data2" />
    <input name="data3" />
    <button type="submit">submit</button>
  </form>
}
Enter fullscreen mode Exit fullscreen mode
Collapse
lukeshiru profile image
Luke Shiru

It can be improved a little bit using FormData:

const submitHandler = event => {
    event.preventDefault();
    const formData = new FormData(event.currentTarget);

    // You can send formData to the back-end using fetch.
    // It arrives there as an object { data1: value1, data2: value2, data3: value3 }
};

const MyComponent = () => (
    <form onSubmit={submitHandler}>
        <input name="data1" />
        <input name="data2" />
        <input name="data3" />
        <button type="submit">submit</button>
    </form>
);
Enter fullscreen mode Exit fullscreen mode

The good thing about using the platform that way, is that you can transfer that knowledge anywhere, not just React. You can literally use that same submitHandler in every library or framework out there (the philosophy that Remix has).

Cheers!

Collapse
rcls profile image
Ossi P.

I always prefer this over controlled inputs. Less code, better performance.

Collapse
noriller profile image
Bruno Noriller Author

Yes, but I needed an example and forms are the easiest one. =p

Collapse
brense profile image
Rense Bakker

Oh yes, definitely agree with the example and the point you're making! I just felt like throwing it out there as an extra gimmick, because a lot of people don't realize sometimes you don't need to use any state at all!

Collapse
bacloud22 profile image
aben • Edited on

For a lazy developer like me, more lean to back-end, what would you suggest to have a semi synchronous front end without diving into React or any reactive programming library, something easy to grasp, two ways binding between front and back.
One example is a toggle button that would hit an api then change say the colour of the current card (toggled).
Basically, this is achievable using fetch and this is how I'm doing it, but is there any solution out there ?

thanks a lot Bruno

Collapse
noriller profile image
Bruno Noriller Author

This is a though one... with React you would have to use useEffect, useState everywhere, and personally, I'm doing the exact opposite where my backend end up as barren as possible.

The one thing that is comming to mind is to use something like Redux.
Every button clicked or action taken would dispatch something and the components would all be consuming that one state.
Even then, you would need fetches. (unless you could pull it off using websockes, but that depends on the type of project you have)

If you're already using javascript for the backend... then you might want to check Remix out because the components can end up pretty light and still react to any actions taken.

Collapse
bacloud22 profile image
aben

I think that's the catch. It should not be a tough one. While everyone is concerned with reactive apps while building a regular website, no one thought let's make a client-UI/Server-api two way binding for years now!

Thread Thread
noriller profile image
Bruno Noriller Author

IMHO, I don't see a reason to put strain on a server when I can just as easily let the client handle all the state and whatever else.
I still need data from an API, but when using JAMStack, it can be basically anything.

The way you want it, it would probably be with websockets. I believe Blazor is doing something that.

Collapse
bacloud22 profile image
aben

Thanks 🙏 Bruno.
I never heard of ReMix but it looks cool. I will try to go through their examples

Collapse
helloitsian profile image
Ian

Another thing you can do for this use case is use useReducer as it provides more clarity for your state management without creating a bunch of useState calls.

Your updateData looks something like this

const updateData = (e) => {
  dispatch({
    type: e.target.name,
    payload: e.target.value,
  })
}
Enter fullscreen mode Exit fullscreen mode

with a reducer...

const formReducer = (state, action) => {
  switch (action.type) {
    case 'data1':
      return { ...state, data1: action.payload }
    case 'data2':
      return { ...state, data2: action.payload }
    case 'data3':
      return { ...state, data3: action.payload }
    default:
      return state
  }
}
Enter fullscreen mode Exit fullscreen mode
Collapse
noriller profile image
Bruno Noriller Author

Not sure about this one...
If you have multiple source of states and each one have a name or something like data-attribute with something unique, you don't even need to know the keys from the function.
The reducer, as you added there is just another point you have to maintain.

Of course... this would be assuming you only need to do the same thing for each one.

And you could still just make { ...state, [action.type]: action.payload } maybe after filtering whatever don't fit the mold.