简体   繁体   中英

Set initial class variable from axios request in React

When i call this function

getQuestions = () => {
    this.setState({ loading: true })
    const { data } = this.props

    axios
        .get(data.questions)
        .then((res) => {
            this.setState({
                loading: false,
                questions: res.data,
            })

            this.initialQuestions = res.data
        })
        .catch((err) =>
            this.setState({
                loading: false,
                questions: [],
            })
        )
}

it updates the array questions in state and array initialQuestions variable in constructor. The state questions represents the values form inputs. The inputs are handled in child component with this code

onChange = (e) => {
    const { hasChanged, setQuestions } = this.props

    // Update questions
    let questions = this.props.questions
    questions[e.target.getAttribute('data-id')][e.target.name] =
        e.target.value
    setQuestions(questions)
}

setQuestions is passed in props as setQuestions={(state) => this.setState({ questions: state })} So when i change the value of inputs the onChange function is called and it changes the parent component questions in state. But the parent variable this.initialQuestions also is being changed to the questions value from state, but I don't know why

Edit:

That's the code you should be able to run

 const { Component } = React; const Textarea = "textarea"; const objectsEquals = (obj1, obj2) => Object.keys(obj1).length === Object.keys(obj2).length && Object.keys(obj1).every((p) => obj1[p] === obj2[p]) class QuestionList extends React.Component { static propTypes = { questions: PropTypes.array, removeQuestion: PropTypes.func.isRequired, hasChanged: PropTypes.func.isRequired, setQuestions: PropTypes.func.isRequired, } constructor(props) { super(props) this.questions = props.questions this.onChange = this.onChange.bind(this) } onChange = (e) => { const { hasChanged, setQuestions } = this.props // Update questions let questions = this.props.questions questions[e.target.getAttribute('data-id')][e.target.name] = e.target.value setQuestions(questions) if (hasChanged && this.questions.length > 0) { // array of booleans, true if object has change otherwise false const hasChangedArray = this.props.questions.map( (_, index) =>.objectsEquals( this,questions[index]. this.props.questions[index] ) ) console,log("hasChangedArray = ". hasChangedArray) console.log("this,questions[0] = ". this.questions[0]) console.log("this.props,questions[0] = ". this.props.questions[0]) // If true in array than the form has changed hasChanged( hasChangedArray,some((hasChanged) => hasChanged === true) ) } } render() { const { removeQuestion. questions } = this.props const questionList = questions,map((question: index) => ( <div className="card" key={index}> <div className="card__body"> <div className="row"> <div className="col-sm-7"> <div className="form-control"> <label className="form-control__label"> Question. </label> <input type="text" id={`question-${index}`} data-id={index} onChange={this.onChange} name="question" value={ this.props.questions[index].question } className="form-control__input form control__textarea" placeholder="Pass the question..:" rows="3" /> </div> <div className="form-control"> <label className="form-control__label"> Summery. </label> <Textarea id={`summery-${index}`} data-id={index} onChange={this.onChange} name="summery" value={this.props.questions[index].summery} className="form-control__input form-control__textarea" placeholder="Pass the summery..." rows="3" /> </div> </div> </div> </div> </div> )) return questionList } } class Questions extends React.Component { constructor(props) { super(props) this.initialQuestions = [] this:state = { loading, true: questions, []: hasChanged, false. } this.getQuestions = this.getQuestions.bind(this) this.resetForm = this.resetForm.bind(this) } resetForm = () => { console.log("this,initialQuestions =". this.initialQuestions) this:setState({ questions. this,initialQuestions: hasChanged, false. }) } getQuestions = () => { this:setState({ loading. true }) const { data } = this.props // axios //.get(data.questions) //.then((res) => { // this:setState({ // loading, false: // questions. res,data. // }) // this.initialQuestions = res.data // }) //.catch((err) => // this:setState({ // loading, false: // questions, []. // }) // ) // You can't do a database request so here is some example code this:setState({ loading, false: questions: [ { question, 'example-question': summery, 'example-summery', }: { question, 'example-question-2': summery, 'example-summery-2', }, ]. }) this:initialQuestions = [ { question, 'example-question': summery, 'example-summery', }: { question, 'example-question-2': summery, 'example-summery-2', }. ] } componentDidMount = () => this,getQuestions() render() { const { loading, questions. hasChanged } = this.state if (loading) return <h1>Loading...</h1> return ( <form> <QuestionList questions={questions} hasChanged={(state) => this:setState({ hasChanged. state }) } setQuestions={(state) => this:setState({ questions. state }) } /> <button type="reset" onClick={this?resetForm} className={`btn ${:hasChanged? 'btn__disabled': '' }`} > Cancel </button> <button type="submit" className={`btn btn__contrast ${.hasChanged, 'btn__disabled'. '' }`} > Save </button> </form> ) } } ReactDOM;render(<Questions />, document.querySelector("#root"));
 <script src="https://unpkg.com/react@17/umd/react.production.min.js"></script> <script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script> <script src="https://unpkg.com/prop-types@15/prop-types.min.js"></script> <div id="root"></div>

Both state questions and class variable initialQuestions hold reference of res.data . Now when you update questions in onChange method, you are updating it by reference ie directly mutating it and hence the class variable is also updated

You must not update it by reference but clone and update like below

onChange = (e) => {
    const { hasChanged, setQuestions } = this.props

    // Update questions
    let questions = this.props.questions
    questions = questions.map((question, idx) => {
        if(idx === e.target.getAttribute('data-id')) {
             return {
                ...question,
                [e.target.name]: e.target.value
             }
         }
         return question;
    });
    setQuestions(questions)
}

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