Juan Reina Pascual
Juan Reina Pascual

Reputation: 4588

setState using promise

I´m trying to use promise to fill a react component. I have debugged and the statement this.setState({items: items}) inside componentWillMount() is called after render() so always the items array in the render function is empty. Also I have tried to use componentDidMount().

Which would it be the right approach?

interface Props extends React.Props<ItemsListComponent> {
    isAddButtonClicked : boolean;
    newItem : string;
    }

    interface State {
      items : Array<ItemEntity>;
    }

    export class ItemsListComponent extends React.Component<Props, State> {

    constructor(props: Props) {
      super(props);
      this.state = {items: []};
    }

    public componentWillMount() {
      itemAPI.getAllItems().then((items) => {
           this.setState({items: items})
      });
    }

    render() {
     return(
      <div className="container">
        <div className="row">
          <ul className="list-group">
            {this.state.items.map((item : ItemEntity) =>
                // Each child in an array or iterator should have a unique "key" prop. React doc.
                <li className="list-group-item" key={item.id}>{item.task}</li>
            )}
          </ul>
        </div>
      </div>
     );
     }
    }

Upvotes: 0

Views: 2756

Answers (5)

Leon
Leon

Reputation: 4532

According to the React Documentation calling APIs is best done in componentDidMount().

That in combination with the answer from Benjamin will give you the best implementation.

Upvotes: 0

Benjamin Gruenbaum
Benjamin Gruenbaum

Reputation: 276286

The problem is that the data is not yet available when React renders the component. In practice, your code should guard against this, either at the component or above:

render() {
  if(!this.state.items) return null; // or loading indicator, data has not returned yet
   return (<div className="container">
      ...
    </div>);
}

Upvotes: 3

Stephen L
Stephen L

Reputation: 2339

From the docs: componentWillMount() is invoked immediately before mounting occurs. It is called before render(), therefore setting state synchronously in this method will not trigger a re-rendering. Avoid introducing any side-effects or subscriptions in this method. You should be able to make your request in componentDidMount(). This ensures a rerender when the state changes. If it's still empty, there's a chance that your map in your render is returning an empty array. Make sure you are properly formatting the item that is returned from your api. There's a chance the response is of the form { items: [] }.

Upvotes: 0

kWeglinski
kWeglinski

Reputation: 411

Create method 'asyncUpdate(data) { this.setState(data) }' in constructor set 'this.asyncUpdate = this.asyncUpdate.bind(this)'

And after your promise call trigger 'this.asyncUpdate(promisedData)'

Should help

Upvotes: 0

nikitaeverywhere
nikitaeverywhere

Reputation: 1343

I would put the itemAPI.getAllItems() in component's constructor. Once the items arrive, no matter when, the state will be updated and this will trigger render().

Upvotes: 0

Related Questions