15

I know I should not mutate state directly in React, but how about situation when I use function:

onSocialClick = e => {
    const id = e.target.value;
    this.setState((prevState, props) => {
        prevState[id] = !(prevState[id]);
        return prevState;
    });
};

Is it a mistake to modify passed object?

EDIT:

It turns out that most of us were wrong here. React docs state it clearly now:

state is a reference to the component state at the time the change is being applied. It should not be directly mutated. Instead, changes should be represented by building a new object based on the input from state and props

Thanks to @Tomáš Hübelbauer for pointing it out in the comment.

Tomasz Mularczyk
  • 34,501
  • 19
  • 112
  • 166
  • 3
    It isn't a mistake :) Not modify state directly means you should use `setState`, not `this.state =...`. If you do - everything is ok, whatever you use as incoming parameter. – elmeister Apr 09 '17 at 10:15
  • @Tomasz I asked a follow up question because I had my doubts about your code snippet. You may find this interesting: https://stackoverflow.com/q/47339643/2715716 – Tomáš Hübelbauer Nov 17 '17 at 08:06
  • 1
    @TomášHübelbauer thanks! I've made an edit. – Tomasz Mularczyk Nov 19 '17 at 08:17
  • I tried to find the exact statement in the docs, but looks like they renamed the variable- `prevState` is now just `state` – Andrew Nessin Nov 11 '18 at 22:48

4 Answers4

3

A cleaner way would be to refer directly to the property you want do edit:

doIt = () => this.setState(({ [id]: prevValue }) => ({
  [id]: !prevValue,
}));
Fez Vrasta
  • 14,110
  • 21
  • 98
  • 160
  • Is it possible to do this if `[id]` is not a direct child of `state`, but one level down? In my case I have an array of `[id]`s, and all other Q/As and articles suggest making copies but I find your solution clearer. – Al.G. Oct 28 '17 at 14:42
  • @Al.G. you will have probably to use `find` to get the right index to access the right array element – Fez Vrasta Oct 28 '17 at 15:44
0

The way you do it is not wrong. Its perfectly okk to modify the argument and then return it. However setState with prevState argument is used when you modify the state based on the value of the prevState and it prefectly fits your scenario and a more cleaner way to handle it would be

onSocialClick = e => {
    const id = e.target.value;
    this.setState((prevState, props) => {
        return {[id] : !(prevState[id])};
    });
};
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
0

No, you should not mutate the prevState in updater function of setState.

Updater function has to be a pure function


The updater function is called twice. See this discussion

Check the following codesandbox and open the console of the codesandbox to see it in action.

Akshay Vijay Jain
  • 13,461
  • 8
  • 60
  • 73
-1

It is highly discouraged to mutate the state directly. Also, you are mutating the previous state and then returnning it which doesnt make much sense. If all you want is to toggle the state at state[id] then fix your code to be more like:

onSocialClick = e => {
    const id = e.target.value;
    this.setState({this.state[id]: !this.state[id});
};
yogi
  • 1,327
  • 2
  • 12
  • 33
  • this is discouraged, you should use the functional way as used by OP (but maybe using directly the interested props. – Fez Vrasta Apr 09 '17 at 11:03