Organizing and setting state from another file

I have some data (objects) that live in their own file (Origin.js).

This data is being exported using the spread operator within another object named OriginState:

Origin.js

//info
const info = {
    title: '',
    year: '',
};

//images
const images = {
    logo: '',
    heroImage: '',
};

//text
const text = {
    header: '',
    body: '',
};

export const OriginState = {
    ...info,
    ...images,
    ...text,
};

I am importing my OriginState object in another file and using it as state for my app like this:

OtherFile.js

import { OriginState } from './Origin.js';

    const [state, setState] = useState({
        ...OriginState,
    });

Here is an example handler where I am using this state to update some specified state values in an input later:

const handleChange = (e) => {
    const { name, value } = e.target;
    setState((state) => ({
        ...state,
        [name]: value,
    }));
};

Now, my question is… Is it incorrect to store state like this?

Additionally, am I using setState incorrectly in my handler function?

In most cases I’ve seen state declared and updated like this which is obviously easier to read:

const [count, setCount] = useState(0);
setCount(count + 1)

But I have a lot of state and didn’t think it would be a good idea to have multiple setState hooks.

Is there a better way to do this? What I currently have just feels wrong.

16 thoughts on “Organizing and setting state from another file”

  1. Your approach seems quite legit, and this is one of the best practices that if your newState is depend on the oldState use setState callback and get the old state from callback input because otherwise as you showed above if you use it like this:

    const [count, setCount] = useState(0);
    setCount(count + 1)
    

    you may increase the chance to get stale data which will increase the potential for bug

    Reply
  2. Is it incorrect to store state like this?

    const handleChange = (e) => {
        const { name, value } = e.target;
        setState((state) => ({
            ...state,
            [name]: value,
        }));
    };
    

    Nope, not at all, in fact, it is often the preferable pattern for state updates.

    Any time your state update depends on the previous state, i.e. the classic counter example, or in your case, when there is nested state, you should use a functional state update to update from the previous state instead of the state from the previous render cycle.

    Additionally, am I using setState incorrectly in my handler function?

    In most cases I’ve seen state declared and updated like this which is
    obviously easier to read:

    const [count, setCount] = useState(0);
    setCount(count + 1)
    

    I see no issue with your state update logic in the handler. In this count example it would (should) be considered incorrect to update a count like this. See this codesandbox demo that attempts to show the issue between non-functional and functional state updates.

    Edit react - regular and functional state updates

    The correct state update should be setCount(count => count + 1)

    But I have a lot of state and didn’t think it would be a good idea to
    have multiple setState hooks.

    Is there a better way to do this? What I currently have just feels
    wrong.

    When it comes to form inputs and state I think it makes sense to have a single flat object. There isn’t really a right or wrong answer in general though when it comes to using a single useState hook with "complex" state shape, or to use a single useState hook for each "chunk" of state. It’s an opinionated answer, mostly do what makes sense for a specific use-case.

    Generally though I’d say if a set of values are even loosely related then perhaps it makes sense to store them in a common object, but this is my opinion.

    A potential issue I see with your imported data though is the chance that you may inadvertently overwrite some key-value pairs by the use of the Spread syntax.

    export const OriginState = {
        ...info,
        ...images, // <-- could overwrite values from info
        ...text, // <-- could overwrite values from info and images
    };
    
    Reply

Leave a Comment