繁体   English   中英

修复未重新渲染的子组件的方法(由于数据的更改是作为props而不是状态传递的)?

[英]Ways to fix a child component that is not re-rendering (due to change in data being passed in as props, not state)?

背景

我正在使用一个使用ReactJS作为渲染库的Meteor应用程序。

目前,我在更新数据时重新渲染子组件时遇到问题,即使父级正在访问更新的数据并且据称将其传递给子级。

Parent组件是一个数据表。 子组件是单击编辑日期字段。

它(理论上)的工作方式 :父组件将日期的现有数据作为prop传递给子组件。 子组件获取现有的道具数据,处理它并使用它设置一些状态,然后有2个选项:

  • 默认:显示数据
  • 如果用户点击数据字段:更改为输入并允许用户选择日期(使用react-datepicker),更改状态 - 当用户在字段外单击时,触发返回仅显示并将更新的数据从状态保存到数据库

我在表中每行使用子组件两次,每次使用它时,它需要访问数据库中的最新日期数据。 因此,如果数据在一个字段中更改,则第二个字段应反映该更改。

问题

除非我手动刷新页面并强制子组件使用新数据进行渲染,否则第二个字段不会反映数据库中的更改。 编辑的字段反映数据的变化,因为它反映了什么是存储在状态。

阅读完React文档之后,我确定问题是因为日期是作为一个prop进入,然后作为一个状态处理 - 并且因为该组件不会从prop更改中重新呈现。

我的问题

我该怎么做才能解决这个问题?

我对文档所做的所有阅读强烈建议远离诸如forceUpdate()和getDerivedStateFromProps()之类的东西,但结果我不确定如何通过我想要的方式传递数据。

思考?

我的守则

我已经将代码缩减了一点并删除了特定于我的项目的变量名称,但是如果它有帮助我可以提供更多的实际代码。 我认为我的问题比直接调试更具概念性。

ParentComponent () {
    //Date comes as a prop from database subscription
    var date1 = this.props.dates.date1 
    var date2 = this.props.dates.date2
    return(
        <ChildComponent 
            id='handle-date-1'
            selected={[date1, date2]} />
        <ChildComponent 
            id='handle-date-2'
            selected={[date1, date2]} />
    )
}

儿童

ChildComponent() {
    constructor(props) {
        super(props);
        this.state = {
            date1: this.props.selected[0],
            date2: this.props.selected[1],
            field: false,
        };
    }

    handleDatePick() {
        //Handles the event listeners for clicks in/out of the div, handles calling Meteor to update database.
    }
    renderDate1() {
        return(
            <div>
                {this.state.field == false &&
                    <p onClick={this.handleClick}>{formatDate(this.state.date1)}</p>}
                {this.state.field == true &&
                    <DatePicker
                        selected={this.state.date1}
                        startDate={this.state.date1}
                        onChange={this.handleDatePick}
                    />
                }
            </div>
        )
    }
    renderDate2() {
        return(
            <div>
                {this.state.field == false &&
                    <p onClick={this.handleClick}>{formatDate(this.state.date2)}</p>}
                {this.state.field == true &&
                    <DatePicker
                        selected={this.state.date2}
                        startDate={this.state.date2}
                        onChange={this.handleDatePick}
                    />
                }
            </div>
        )
    }
    render() {
        return(
            //conditionally calls renderDate1 OR renderDate2
        )
    }
}

(如果这个代码/我的解释很粗糙,那是因为我还是一个相当初学者/低级别的开发人员。我没有接受过正式培训,所以我在学习一个非常困难的应用程序时正在学习这个工作。独唱开发者。这是一个很长的故事。请温柔!)

React文档有一节关于constructor()最佳实践用法。 阅读,密切注意黄色突出显示的“注意”部分,应该说明你遇到的确切问题。

本质上, constructor()只运行一次,最常用于初始化内部/本地状态(或绑定方法)。 这意味着你的子组件设置date1date2从您的价值观selected道具时, 一次 constructor()被调用那个孩子。 无论selected的值是什么,当constructor()被调用时将被设置为子状态,并且即使值继续改变也将保持不变。

因此,传递给子组件的selected prop的任何连续更新都不会反映在该组件的内部状态中,这意味着不会重新呈现该组件。 您需要在子组件的其他位置使用React的setState()方法来正确更新该子级的状态并触发重新呈现。

使用React生命周期方法的组合来正确更新子组件是可行的方法。 下面的代码片段为您提供了有关在componentDidMount()componentDidUpdate()实现更改的主要想法

 class Child extends React.Component { constructor(props) { super(props); this.state = { date1: 0, date2: 0, }; } /* Any time the child mounts to the DOM, you can use the method below to set your state using the current value of your selected prop from the parent component... */ componentDidMount() { this.setState({ date1: this.props.selected[0], date2: this.props.selected[1] }); } /* When the child receives an update from its parent, in this case a new date selection, and should re-render based on that update, use the method below to make a comparison of your selected prop and then call setState again... */ componentDidUpdate(prevProps) { if (prevProps.selected !== this.props.selected) { this.setState({ date1: this.props.selected[0], date2: this.props.selected[1] }); } } render() { const { date1, date2 } = this.state; return ( <div style={{ border: `4px dotted red`, margin: 8, padding: 4 }}> <h1 style={{ fontWeight: 'bold' }}>Child</h1> <h2>The value of date1 is {date1}</h2> <h2>The value of date2 is {date2}</h2> </div> ); } } class Parent extends React.Component { constructor(props) { super(props); this.state = { valOne: 0, valTwo: 0 }; } incrementOne = () => { this.setState(prevState => ({ valOne: (prevState.valOne += 1) })); }; incrementTwo = () => { this.setState(prevState => ({ valTwo: (prevState.valTwo += 1) })); }; render() { const { valOne, valTwo } = this.state; return ( <div style={{ border: `4px solid blue`, margin: 8, padding: 4 }}> <h1 style={{ fontWeight: 'bold', fontSize: 18 }}>Parent</h1> <button onClick={() => this.incrementOne()}>Increment date1</button> <button onClick={() => this.incrementTwo()}>Increment date2</button> <Child selected={[valOne, valTwo]} /> </div> ); } } ReactDOM.render(<Parent />, document.querySelector('#app')); 
 <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <div id="app"></div> 

由于您正在为您的孩子使用React Component模式,因此充分利用React生命周期方法将真正帮助您。 我不能强烈建议你学习React组件的生命周期 当你继续使用React时,在这种情况下它将变得至关重要。 componentDidMount()componentDidUpdate()是很好的起点。

希望这会有所帮助。 让我们知道结果如何。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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