1

I am experimenting with React: I have 2 arrays (array1 and array2) containing some words. I would like to initialize my React state array called "sentences" by looping the 2 arrays (array1 and array2) and pushing a component "Sentence" into the state array at each iteration. This is my code:

import React, { Component } from "react";
import "./App.css";
import Sentence from "./Sentence.js";

var array1 = ["hello", "some", "words", "house", "garden", "car"];
var array2 = ["other", "bag", "of", "words", "oh", "yeah"];

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      sentences: []
    };
  }

  componentDidMount() {
    for (var i = 0; i < array1.length; i++) {
      for (var j = 0; j < array2.length; j++) {
        let newArray = this.state.sentences.slice();
        newArray.push( <Sentence word1={array1[i]} word2={array2[j]} /> );
        this.setState({ sentences: newArray });
      }
    }
  }

  render() {
    return (
      <div>
        {this.state.sentences[0]}
        {this.state.sentences[1]}
        {this.state.sentences[2]}
      </div>
    );
  }
}

export default App;

And this is Sentence.js:

import React, { Component } from "react";

class Sentence extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      word1: this.props.word1,
      word2: this.props.word2
    };
  }

  render() {
    return (
      <div>
        First word: {this.state.word1}
        <br />
        Second word: {this.state.word2}
      </div>
    );
  }
}
export default Sentence;

However, in browser, I only see:

First word: car
Second word: yeah

My desired result would be to see the first and second word for the first 3 components of the state array (sentences).

2
  • 1
    setState is async. this.state.sentences won't be updated inside the loop. Build the Array first and then push it all at once. Commented Feb 21, 2019 at 11:23
  • Even if it weren't async, you'd keep overwriting the state with your last Sentence. codesandbox.io/s/7382nzzzr0 (check index.js) Commented Feb 21, 2019 at 11:26

1 Answer 1

1

setState is async. this.state.sentence won't be updated inside the loop.

Either Build the Array first and then push it all at once.

let sentences = this.state.sentences.slice();

for (var i = 0; i < array1.length; i++) {
  for (var j = 0; j < array2.length; j++) {
    sentences.push( <Sentence word1={array1[i]} word2={array2[j]} /> );
  }
}

this.setState({ sentences: sentences });

or use the callback version of setState

for (let i = 0; i < array1.length; i++) {
  for (let j = 0; j < array2.length; j++) {
    this.setState((state) => {
      return {
        sentences: [
          ...state.sentences, 
          <Sentence word1={array1[i]} word2={array2[j]} />
        ]
      }
    });
  }
}

And as a sidenote, your Sentences Component doesn't need a state:

const Sentence = ({ word1, word2 }) => (
  <div>
    First word: {word1}
    <br />
    Second word: {word2}
  </div>
);
export default Sentence;

I'd build it like this

var array1 = ["hello", "some", "words", "house", "garden", "car"];
var array2 = ["other", "bag", "of", "words", "oh", "yeah"];

class App extends Component {
  constructor(props){
    super(props);

    this.state = { pairs: [] };
    for(const word1 of array1) {
      for(const word2 of array2) {
        this.state.pairs.push({ word1, word2 });
      }
    }
  }

  render() {
    return (
      <div>{
        this.state.pairs.map(words => <Sentence ...words />)
      }</div>
    );
  }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Regarding this.state.pairs.push({ word1, word2 }); Shouldn't this.state be treated as if it were immutable?
@Tom, you're right I'm not in the constructor anymore. fixed it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.