简体   繁体   中英

Remove items from an array React?

I am making a form that allows a user to build a quiz (here's my code so far)

var uuid = require("uuid-v4");
// Generate a new UUID
var myUUID = uuid();
// Validate a UUID as proper V4 format
uuid.isUUID(myUUID); // true

var questionNum = 0;

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      key: uuid(),
      title: "",
      author: "",
      questions: [],
      answers: []
    };

    this.handleChange = this.handleChange.bind(this);
    this.addQuestion = this.addQuestion.bind(this);
  }

  componentDidMount() {
    // componentDidMount() is a React lifecycle method
    this.addQuestion();
  }

  handleChange(event) {
    const target = event.target;
    const value = target.type === "checkbox" ? target.checked : target.value;
    const name = target.name;

    this.setState({
      [name]: value
    });
  }

  /**
   * It's probably better to structure your questions like this:
   * this.state.questions: [{
   *         question: 'How are you?',
   *         answers: ['Good', 'Great', 'Awful'],
   *         // correctAnswer: 'Great'
   *     },
   *     {
   *         question: 'What is your name?',
   *         answers: ['Toby', 'Marco', 'Jeff'],
   *         // correctAnswer: 'Jeff'
   *     }];
   *
   * This allows you to keep better track of what questions
   * have what answers. If you're making a 'quiz' type structure,
   * you could additionally add a `correctAnswer` property.
   */

  addQuestion() {
    questionNum++;
    this.setState(previousState => {
      const questions = [...previousState.questions, "question", "hi"];
      const answers = [...previousState.answers];

      for (var i = 0; i < 4; i++) {
        answers.push({
          answerChoice: "",
          key: uuid()
        });
      }
      return { questions, answers };
    });
    console.log(
      this.state.answers,
      this.state.questions,
      questionNum,
      this.state.title,
      this.state.author
    );
  }

  render() {
    return (
      <div className="App">
        <div>
          <header className="App-header">
            <img src={logo} className="App-logo" alt="logo" />
            <h1 className="App-title">Quiz Form 2.0</h1>
          </header>
          <p className="App-intro">
            To get started, edit <code>src/App.js</code> and save to reload.
          </p>
        </div>

        <div>
          <form>
            <div className="Intro">
              Give your Quiz a title:{" "}
              <input
                type="text"
                value={this.state.title}
                onChange={this.handleChange}
                name="title"
              />
              <br />
              Who's the Author?{" "}
              <input
                type="text"
                value={this.state.author}
                onChange={this.handleChange}
                name="author"
              />
              <br />
              <br />
            </div>
            <div className="questions">
              Now let's add some questions... <br />
              {// This is where we loop through our questions to
              // add them to the DOM.
              this.state.questions.map(question => {
                return <div>{question}</div>;
              })

              // This is what it would look like for the structure
              // I proposed earlier.
              // this.state.questions.map((question) {
              //   return (
              //       <div>{question.quesion}</div>
              //       {
              //           question.answers.map((answer) => {
              //               return (<div>{answer}</div>);
              //           })
              //       }
              //   );
              // })
              // This would output all questions and answers.
              }
            </div>
          </form>
          <button onClick={this.addQuestion}>Add Question</button>
        </div>
      </div>
    );
  }
}

export default App;

And I have come to the point where I want to try and be able to "Remove" a question (using a button). What my code does now is it adds objects to an array, and I have got that figured out. But now I am trying to remove Items from the array. I was thinking "ok just remove the last question" but realistically users will want to remove any of their questions. I was just curious if anyone had some tips for this, I really don't know how to start.

If you want the user to be able to remove any question, add an onClick to the question div (or a child of the div - remember to move the onClick ). The callback for that can accept an index, which refers to the element in the list to remove.

Example:

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

    this.removeItem = this.removeItem.bind(this)
  }

  removeItem (index) {
    this.setState(({ questions }) => {
      const mQuestions = [ ...questions ]
      mQuestions.splice(index, 1)
      return { questions: mQuestions }
    })
  }

  render () {
    return (
      <div>
        ...
        { this.state.questions.map((question, index) => {
          return <div onClick={ () => this.removeItem(index) }>{ question }</div>
        }) }
      </div>
    )
  }
}

This isn't so much a react question as a JavaScript question. Since your questions are stored in your react state it will update the DOM when that state is modified. Simply removing the value from the array using this.setState() will suffice.

You have a few options for removing values from your array. The main thing to keep in mind here is to make sure you don't modify the actual array but rather replace it with a new instance of an array. Modifying the array directly will not trigger updates and goes against the general principles of react state. For example, using Array.prototype.splice() would modify your original array. ( Array.prototype.splice docs )

In JavaScript, primatives such as strings and numbers are passed by value however objects such as arrays, sets, or generic JavaScript objects are passed by reference. This means that by assigning an object to a new variable you will now have two variables pointing at the same object.

const foo = [1];
const bar = foo;
console.log(foo); // [1]
console.log(bar); // [1]
foo.push(2);
console.log(foo); // [1, 2]
console.log(bar); // [1, 2]

One common way to get around this is by using the ES6 spread notation ( Spread operator docs ) to spread the values into a new array. const bar = [...foo] would return a copied array pointing to a separate object. Applying this to your question, you could do const q = [...this.state.questions] and then modify q using q.splice(index, 1) and assign that to your state using this.setState() . There are obviously other options to removing items from an array and I suppose it is not a given that you know the index of the array. In this case, tools such as Array.prototype.find() or Array.prototype.findIndex() are helpful or you could use a JavaScript Map object ( Map docs ) in place of your array to eliminate the need for indexes while maintaining question order. All of these options are equally valid so I'll leave it to you to determine how you want to go about it.

In order to actually trigger the deletion you will need to have some sort of user control on the page. You can do this with click listeners on elements, specific buttons included in the HTML for each question, or maybe even a dropdown menu elsewhere for deletion. The end result will be that the element the user interacts with will maintain a unique ID so that when it activates the callback function you can determine which question it is that should be deleted. This ID will be stored in the first argument of your function. In most examples this will be named "event".

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM