1

I'm trying to replace an initially empty array with an array made by a function using setState in React. I have in my constructor as follows:

this.state = {
    sources: []
};

This displays the correct array (I tested it with [0,1,2]). However, when I try to setState in a method like so:

this.setState({
    sources: [0,1,2]
})

it does not work and still displays the empty (or original) array. I understand you cannot mutate an array as state directly in React, but I don't think that's what I'm doing here. Here are some other things I have tried when researching this question, none of which have worked:

this.setState({
    sources: [...this.state.sources, 1]
})

...

this.setState({
    sources: this.state.sources.concat('test')
})

...

var temp = [0,1,2]
this.setState({
    sources: temp
})

Thanks in advance for any help!

EDIT Full code:

export class SrcMenu extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            sources: [],
        };

        this.searchSources = this.searchSources.bind(this);
    }

    searchSources() {
    // commented out the actual method to replace with test code
        var temp=[0,1,2];
        console.log(temp); // [0,1,2] so I know the method works
        this.setState({
            sources: temp // etc for the other things i tried
        })
        console.log("this.state.sources: " + this.state.sources); // empty
    }

    render() {
        return(
        // etc...
          <button type="button" onClick={this.searchSources}>Test</button>
        );
    }
}
Leo T
  • 11
  • 2
  • 1
    works here - https://codesandbox.io/s/l7j005yknz – Paul Fitzgerald Jul 17 '18 at 14:15
  • 1
    It should not be `this.state({ sources: [] };` in the constructor, but `this.state = { sources: [] };` – Tholle Jul 17 '18 at 14:17
  • Could you post a snippet of the method you are calling the setState from – Sebastiaan van Arkens Jul 17 '18 at 14:21
  • @Tholle thank you, that was a typo in my question, in the actual code file I have it written correctly – Leo T Jul 17 '18 at 14:21
  • Alright. Could you include your entire component that has this behavior? It's hard to say what might be wrong from the code currently in your question. – Tholle Jul 17 '18 at 14:22
  • are you sure you are calling that method ? where is that method ? how are you calling it ? when are you checking to see if `state` has changed ? – Mihai T Jul 17 '18 at 14:23
  • edited to include full test code – Leo T Jul 17 '18 at 14:31
  • You are trying to access it right after setting state, it does not work that way. `setState` is async. – Rikin Jul 17 '18 at 14:33
  • @Rikin wow thank you, I had no idea. just tried to call from outside the method and it worked perfectly. thanks so much!!!!!! – Leo T Jul 17 '18 at 14:35
  • that's exactly why i asked `when are you checking to see if state has changed` . `setState` is async so if you check it right after, you won't see the difference although the changes to the state have been made read more here [async setState](https://stackoverflow.com/questions/30782948/why-calling-react-setstate-method-doesnt-mutate-the-state-immediately) – Mihai T Jul 17 '18 at 14:36
  • Possible duplicate of [Why calling react setState method doesn't mutate the state immediately?](https://stackoverflow.com/questions/30782948/why-calling-react-setstate-method-doesnt-mutate-the-state-immediately) – Mihai T Jul 17 '18 at 14:40

2 Answers2

3

Your code is working, but you don't take into consideration that setState is asynchronous. If you log this.state in the callback that can be given as second argument to setState it will guarantee it has updated.

this.setState({ sources: temp }, () => {
  console.log(this.state.sources);
});
Tholle
  • 108,070
  • 19
  • 198
  • 189
  • We see the same question nearly everyday. Why does not people use render method to log the state? I hold myself from answering "Use the render method" instead of this callback one :) – devserkan Jul 17 '18 at 14:39
  • @devserkan I fell for this myself back when I started using React. I think it's better now, but it's not all that obvious from the documentation that it is asynchronous. – Tholle Jul 17 '18 at 14:41
  • 1
    Then, I'm the lucky one I guess :). Yes documentation is not so clear, I think I followed some good tutorials this is why I did not fell into this problem. – devserkan Jul 17 '18 at 14:47
-2

Here is an example that should work as you describe:

class ArrayTest extends React.Component {

  constructor() {
    super();
    this.state = {
      sources: [],
    };
  }

  public render() {

    return (
      <div>
      <div>test: {this.state.sources}</div>
      <button onClick={this._changeValue}>test</button>
       </div>
    );
  }

  private _changeValue = () => this.setState( {sources: [1,5] } );  
}

ReactDOM.render( 
  <ArrayTest />,
  document.getElementById('content')
);
Lupine
  • 1
  • 1