How to avoid mutating state (React)

I have a function that when it is called is passed the initialState and the function to update the state (set through react hooks)

const [initialState, setInitialState] = useState({name: "initial name"})

const toggleValue = (initialState, setInitialState) => {
   const myVal = initialState;
   myVal["name"] = "This is a new name"
}

However I’m noticing that when I run this function initialState is changing to This is a new name as well as myVal. Is there a way to avoid mutating the state? My goal is to update a property in the initialState object and then update the state.

Note that initialState in the first line is a string, not an object. You just need to use setInitialState("This is a new name") to update it.

Why did you call your stateHook “setInitialState”? The initial state is passed when you executed useState("name") and that’s the equivalent to the old setInitialState

That function updated the state asynchronously with whatever you pass it, and initialState (which should be named just state) is the current value of the state.

As the person above me said, initialState is a string (since your initial state was a string and I imagine it’s gonna be a string forever) and the first value of it is "name". To update the state you just call the function with a new value.

It doesn’t mutate. The underlying implementation will do some mutation at some point but that’s opaque to you.

  1. initialState starts out as having a value of [the literal string] “name”.
  2. you run the setInitialState function, which replaces the value.
  3. the component rerenders with a new value for initialState.

You are just calling a function (the JSX is a function call) with a new argument (whatever initialState now is).

And whether it mutates or not is kinda irrelevant, because once you’ve rerendered, you’re discarding the previous state of initialState.

I think you’re misunderstanding the useState API as well (apologies if not): it doesn’t create an object called “name”. It just returns a reference to whatever you give to it + a function for updating that value.

useRef is for mutable internal values (that don’t trigger a rerender).

Sorry, I mistyped my code (updated now)

What I’m passing to useState is an object.

But when I console.log the value of initialState it is mutated, despite never calling the setInitialState function.

Ah, yeah, that’s because you’re ignoring the setter function and just mutating a value. Nothing will really work properly, because you’re doing it outside any React lifecycles so afaik the value is never going to be persisted anywhere and will get blown away as soon as anything rerenders, but it’s just JavaScript, it’s not some specific React convention: you have an object, and you are changing one of its properties.

It’s almost exactly the same as writing

const initialState = { name: "Dan" };
const toggleValue = (newValue) {
  initialState.name = newValue;
}

It’s also the same as, in a class component, doing

state = {
  name: "Dan"
}

toggleName = (name) => {
  this.state.name = name;
}

As I say, it won’t work properly, that’s not how React works. It may kinda work, because you’re using a value that is persisted in a special context within the React runtime, but even if it does you’ll start to get bizarre issues because you’re trying to circumvent how React functions.

Edit: objects in JS are passed by reference. You aren’t creating a copy here, it’s the exact same object:

const myVal = initialState;