1

I am very stuck. I am building a react flash card app and have asked a couple questions regarding setting state when state is an array of objects. I have been playing around and researching different solutions and I appear to be extremely close, but I just can't seem to get this code working properly. Here is my component:

import React, { Component } from 'react'

export class TermForm extends Component {
    state = { 
        allTerms: [
        {
            term: '',
            def: ''
        },
        {
            term: '',
            def: ''
        },
        {
            term: '',
            def: ''
        },
        {
            term: '',
            def: ''
        },
        {
            term: '',
            def: ''
        },
        {
            term: '',
            def: ''
        },
        {
            term: '',
            def: ''
        },
        {
            term: '',
            def: ''
        },
        {
            term: '',
            def: ''
        },
        {
            term: '',
            def: ''
        }
    ]
}
    onChangeTerm = (event, index) => {
        event.preventDefault();
        this.setState({
            allTerms : [
            ...this.state.allTerms.splice(0,index) ,
            {
                ...this.state.allTerms[index],
                term: event.target.value
            },
            ...this.state.allTerms.splice(index+1) ,
            ]
        })
    }

    onChangeDef = (e, index) => {
        e.preventDefault();
        this.setState({
            allTerms : [
            ...this.state.allTerms.splice(0,index) ,
            {
                ...this.state.allTerms[index],
                def: e.target.value
            },
            ...this.state.allTerms.splice(index+1)
            ]
        })
    }

    render() {
        return (
            this.props.numberOfTerms.map((index) => (
                <div key={index}>
                    <input type="text" placeholder="TERM" value={this.state.allTerms.term} onChange={(event) => this.onChangeTerm(event, index)}></input>
                    <input type="text" placeholder="DEFINITION" value={this.state.allTerms.def} onChange={(e) => this.onChangeDef(e, index)}></input>
                </div>
            ))
        )
    }
}

export default TermForm

For context, the return statement in my render method is returning a dynamic form. Basically the rendered form can have anywhere from one to ten lines (each line consisting of an input field for what term the user wants to add, and another input field for the definition) based on how many terms the user wants to add. I have separate onChange events for the term and the definition. When I set state for the first term (so going through the onChangeTerm and onChangeDef functions) the state is set just fine. For any number of cards attempted to be created after the first one, however, the state gets all messed up. By viewing the state in chrome's react tools, I can see that after trying to create a second card (and any number of cards after the first) the term is set, but then when I go to type in a definition, it clears the term value. I honestly am very confused. I am thinking that the problem is in one or both of my onChange functions, but I just can't seem to figure out what the problem is. Any help sure would be appreciated.

4
  • What's numberOfTerms, an array just to specify the count? Commented May 24, 2020 at 21:45
  • Correct. That prop holds the number of terms the user was wanting to add. It is simply an array with the same number of elements as the number of terms the user wants to add. Commented May 24, 2020 at 21:48
  • So it's a number or an array? ;-) Commented May 24, 2020 at 21:50
  • It is an array of numbers. For example, if a user wanted to add 4 terms, the array would look like this: [0, 1, 2, 3]. My point in creating this array was so I could run the map function over this array. The array always holds the same number of elements as the number of terms the user wanted to create. Commented May 24, 2020 at 21:52

1 Answer 1

2

I propose to store the array in a parent component and use the TermForm component to display each term+def individually. When you then update one term/def it sets the new state and also propagates the information to the parent: https://codesandbox.io/s/pensive-morning-95cmx

I also render the app state to display that it works ;) One could also improve the creation of the allTerms array. e.g. populate it with a loop

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

export default class App extends Component {
  state = {
    allTerms: [
      {
        term: "",
        def: ""
      },
      {
        term: "",
        def: ""
      },
      {
        term: "",
        def: ""
      },
      {
        term: "",
        def: ""
      },
      {
        term: "",
        def: ""
      }
    ]
  };
  updateTermDef = (term, def, idx) => {
    const newTerms = [...this.state.allTerms];
    newTerms[idx] = {
      term,
      def
    };
    this.setState({ allTerms: newTerms });
  };
  render() {
    return (
      <div className="App">
        {this.state.allTerms.map((termDef, idx) => (
          <TermForm
            term={termDef.term}
            def={termDef.def}
            idx={idx}
            updateTermDef={(term, def) => this.updateTermDef(term, def, idx)}
          />
        ))}
        <h1>App state: </h1>
        {this.state.allTerms.map(termDef => (
          <>
            <span>
              {termDef.term} : {termDef.def}
            </span>
            <br />
          </>
        ))}
      </div>
    );
  }
}
//TermForm.js

import React, { Component } from "react";

export class TermForm extends Component {
  state = {
    term: this.props.term,
    def: this.props.def
  };

  render() {
    return (
      <div key={this.props.idx}>
        <input
          type="text"
          placeholder="TERM"
          value={this.state.term}
          onChange={event =>
            this.setState(
              { term: event.currentTarget.value },
              this.props.updateTermDef(this.state.term, this.state.def)
            )
          }
        />
        <input
          type="text"
          placeholder="DEFINITION"
          value={this.state.def}
          onChange={event =>
            this.setState(
              { def: event.currentTarget.value },
              this.props.updateTermDef(this.state.term, this.state.def)
            )
          }
        />
      </div>
    );
  }
}
Sign up to request clarification or add additional context in comments.

Comments

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.