ReactJs不更新嵌套组件

时间:2017-07-22 20:02:37

标签: forms reactjs tabs

我知道这是最常见的反应问题之一:“为什么状态不能正确更新我的组件?”而且我已经搜索了很多东西并尝试了很多东西,但似乎无法解决这个问题。

这个问题最常见的答案是我在某个地方改变状态,但我无法发现它。

我正在尝试使用标签创建一个表单,根据我发现的这篇文章,我也可以动态添加表单字段:https://goshakkk.name/array-form-inputs/

一切正常,但是当我删除动态添加的表单时,表单数组的值不会根据状态更新。

我的代码如下:

import React, { Component } from 'react';

import EditorFooter from '../../editor/moduleEditor/Footer';
import EditorHeader from '../../editor/moduleEditor/Header';


class Tab extends Component {

    render() {

        const currentTabNumber = this.props.currentTabNumber;
        const cssClass = currentTabNumber === this.props.number ? 'active' : '';

        return (
            <button className={`tab ${cssClass}`} onClick={(e) => this.props.switchTab(this.props.number)}>{this.props.name}</button>
        )
    };
};

class CourseForm extends Component {

    render() {
        const index = this.props.index;

        return (
            <div className="inner-form">
                <h3>Kurs {index}</h3>
                <button className="removeForm" onClick={this.props.handleRemoveCourse(index)}>Remove form</button>

                <h4>Kursens namn</h4>
                <input type="text" name="name" defaultValue={this.props.course.name} onChange={this.props.handleInputChange(index)} />

                <h4>Årtal</h4>
                <p className="helper-text">Exempel: 2002-2003</p>
                <input type="text" name="years" defaultValue={this.props.course.years} onChange={this.props.handleInputChange(index)} />
            </div>
        )
    };
};


class Editable extends Component {

    constructor(props) {
        super(props);

        this.state = {
            showTab: 1,
        };
    }

    switchTab = (tabNr) => {
        this.setState({ showTab: tabNr });
    }

    render() {

        const courses = this.props.courses;

        const coursesList = courses.map((course, index) =>
            <CourseForm key={index} index={index} course={course} handleInputChange={this.props.handleCourseChange} handleRemoveCourse={this.props.handleRemoveCourse} />
        );

        return (
            <div id={`${this.props.moduleItem.ModuleId}_Editor`} className="form notransition">
                <EditorHeader templateName="Simple test form" />
                <section className="form-inner">
                    <div className="tabs">
                        <Tab switchTab={this.switchTab} currentTabNumber={this.state.showTab} number={1} name="Information" />
                        <Tab switchTab={this.switchTab} currentTabNumber={this.state.showTab} number={5} name="Kurser" />
                    </div>
                    {this.state.showTab === 1 ?
                        <div className="tab-block">

                            <h4>Bild</h4>
                            <p className="helper-text">Tänk på att cv-bilden ska vara av bra kvalitet. Låt gärna en fotograf komma och fota hela projektgruppen.</p>
                            <input type="file" name="image" defaultValue={this.props.image} onChange={this.props.handleInputChange} />

                            <h4>Slogan</h4>
                            <p className="helper-text">Om du skulle skriva en kort reklamslogan om dig själv, vad skulle det vara?</p>
                            <textarea name="slogan" defaultValue={this.props.slogan} onChange={this.props.handleInputChange}></textarea>

                        </div>
                        :
                        null
                    }
                    {this.state.showTab === 5 ?
                        <div className="tab-block">
                            {coursesList}
                            <button className="addForm" onClick={this.props.handleAddCourse}>Add form</button>
                        </div>
                        :
                        null
                    }
                </section>
                <EditorFooter {...this.props} submitForm={this.props.saveForm} />
            </div>
        );
    }
};

class Readable extends Component {
    render() {
        const props = this.props;

        return (
            <section className="module align-top notransition">

            </section>
        );
    }
};

class Template extends Component {
    render() {
        const props = this.props;

        return (
            <section className="module align-top">

            </section>
        );
    }
};


class SimpleTestForm extends Component {

    constructor(props) {
        super(props);

        this.state = {
            slogan: "",
            image: "",
            courses: [{ name: '', years: '' }],
        };
    }

    onAddCourseForm = (event) => {
        this.setState({
            courses: this.state.educations.concat([{ name: '', years: '' }])
        });
    }

    handleCourseChange = (index) => (e) => {
        const newCourses = this.state.courses.map((course, newIndex) => {
            if (index !== newIndex) return course;

            const target = e.target;
            const value = target.type === 'checkbox' ? target.checked : target.value;
            const name = target.name;

            return { ...course, [name]: value };
        });

        this.setState({ courses: newCourses });
    }

    handleRemoveCourse = (index) => (e) => {
        this.setState({
            courses: this.state.courses.filter((courses, newIndex) => index !== newIndex)
        });
        //this.setState( this.state );
    }

    handleAddCourse = () => {
        this.setState({
            courses: this.state.courses.concat([{ name: '', years: '' }])
        });
    }

    saveForm = (e) => {
        console.log(this.state);
    }

    handleInputChange = (e) => {

        const target = e.target;
        const value = target.type === 'checkbox' ? target.checked : target.value;
        const name = target.name;

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

    render() {

        const inEditMode = this.props.mode === 'edit';
        const inTemplateMode = this.props.mode === 'template';
        const inReadMode = this.props.mode === 'read';

        return (
            <div>
                {inEditMode ?
                    <Editable {...this.props}
                        handleInputChange={this.handleInputChange}
                        onChangeEditor={this.onChangeEditor}
                        saveForm={this.saveForm}

                        handleCourseChange={this.handleCourseChange}
                        handleAddCourse={this.handleAddCourse}
                        handleRemoveCourse={this.handleRemoveCourse}

                        courses={this.state.courses}
                        slogan={this.state.slogan}
                        image={this.state.image}
                    />
                    :
                    null
                }

                {inReadMode ?
                    <Readable {...this.props}
                    />
                    :
                    null
                }

                {inTemplateMode ?
                    <Template {...this.props}
                    />
                    :
                    null
                }
            </div>
        );
    }
};

module.exports = SimpleTestForm;

我知道它有点长,我试图把它煮沸。

我的麻烦是,如果我在“Kruser”选项卡下添加三个表单,然后填写它们,然后删除中间的表单,删除正确的表单(我可以看到,如果我记录状态),但它的数据来自它下面的表格。在我切换标签并返回Kurser标签之前,它会显示正确的数据。

1 个答案:

答案 0 :(得分:2)

您不应将数组索引用作键。这是我可以识别的可能导致您的问题的一件事。

如果我的数据没有唯一标识符,我经常使用shortid生成一个。首先yarn add shortidnpm i --save shortid

import { generate } from 'shortid';

// Update, it seems to work better if the unique id is generated on the object itself

...

handleAddCourse = () => {
    this.setState({
        courses: this.state.courses.concat([{ name: '', years: '', itemKey: generate() }])
    });
}

...

const coursesList = courses.map((course, index) =>
            <CourseForm
                key={course.itemKey}
                index={index}
                course={course}
                handleInputChange={this.props.handleCourseChange} 
                handleRemoveCourse={this.props.handleRemoveCourse} />
        );

希望这可以解决你的问题,如果不是,至少你听说过这种反模式。 Here您可以通过示例找到介绍此主题的文章。

另一个提示是,有一种更简洁,更清晰的方式来编写这些星球。

有效,但难以阅读

{inReadMode ?
    <Readable {...this.props}
    />
    :
    null
}

更清洁

{inReadMode &&
    <Readable {...this.props} />
}

请参阅react docs