XorOrNor
XorOrNor

Reputation: 8978

React doesn't update component after setState

I have a problem with updating component in React. My Autocomplete component has its defaultValue property which is linked to this.state.tags. At the time of executing render() method the this.state.tags array is not yet fetched, so it's set empty in the component. When this.state.tags array is set to it's fetched value the Autocomplete is not updated by the React.

constructor(props) {
  super(props);
  this.state = {
    tags:[],
    all_tags:[{tag: "init"}]
  };
}
componentDidMount() {

axios.post('http://localhost:1234/api/issue/getIssueById', {id: this.props.match.params.id}, { withCredentials: true })
  .then(res=>{
  var arr = [];
  res.data.tags.forEach(x=>{
      arr.push({tag: x});
  });
  this.setState((state,props)=>{return {tags: arr}});
})
.catch((e)=>{console.log(e)});
}
render() {

return (
  <Fragment>
      <Autocomplete
             multiple
             defaultValue={this.state.tags[0]}
             onChange={(event, value) => console.log(value)}
             id="tags-standard"
             options={this.state.all_tags}
             getOptionLabel={option => option.tag}
             renderInput={params => (
               <TextField
                 {...params}
                 variant="standard"
                 label="Multiple values"
                 placeholder="Favorites"
                 fullWidth
               />
             )}
           />
      </Fragment>
    );
}

Edit: If I put this inside render():

    setTimeout(()=>{
      console.log("this.state.tags: ", this.state.tags);
    }, 1000);

this.state.tags is set correctly.

Upvotes: 0

Views: 145

Answers (2)

SuleymanSah
SuleymanSah

Reputation: 17858

First, you need to use this.state.tags as options in Autocomplete.

Second your usage of setState seems problematic.

And lastly, if you need to populate all the tags in the render, you need to use value property of the Autocomplete component.

import React, { Fragment, Component } from "react";
import { fetchTags } from "./fakeApi";
import Autocomplete from "@material-ui/lab/Autocomplete";
import TextField from "@material-ui/core/TextField";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      tags: [],
      selectedTags: [],
      all_tags: [{ tag: "init" }]
    };
  }

  componentDidMount() {
    fetchTags()
      .then(res => {
       let allTags = res.data.tags.map(el => ({ tag: el }));
        this.setState({
          tags: allTags,
          selectedTags: allTags
        });
      })
      .catch(e => {
        console.log(e);
      });
  }
  onChange = value => {
      this.setState({
        selectedTags: value
      })
  }

  render() {
    return (
      <Fragment>
        <Autocomplete
          multiple
          value={this.state.selectedTags}
          onChange={(event, value) => this.onChange(value)}
          id="tags-standard"
          options={this.state.tags}
          getOptionLabel={option => option.tag}
          renderInput={params => (
            <TextField
              {...params}
              variant="standard"
              label="Multiple values"
              placeholder="Favorites"
              fullWidth
            />
          )}
        />
      </Fragment>
    );
  }
}

export default App;

Working Codesandbox sample with a fake api.

Upvotes: 0

Mateusz Krzyżanowski
Mateusz Krzyżanowski

Reputation: 582

You are using options={this.state.all_tags} and in the componentDidMount you are updating tags field in the state. I think there is the issue.

Upvotes: 2

Related Questions