ReactJS setState与getDerivedStateFromProps冲突

时间:2018-06-05 15:00:49

标签: reactjs react-redux

我正在尝试遵循https://github.com/reactjs/rfcs/issues/26中的设计模式(请参阅bvaughn的回复)并创建ReactJS可编辑表单,从服务器读取数据,然后在表单字段中显示,允许使用编辑值和然后将这些数据保存回数据库。

我有一些必要的代码:

const mapStateToProps = state => {
    return {
        invoice: state.invoice,
        invoiceReady: state.invoiceReady,
    };
};

 static getDerivedStateFromProps(nextProps, prevState) {
        if (nextProps.match.params.id !== prevState.id) {
            return update(
                prevState, {
                    ['id']: {$set: nextProps.match.params.id},
                    ['invoiceReady']: {$set: false}
                });
        } else {
            return update(
                prevState, {
                    ['invoice']: {$set: nextProps.invoice},
                    ['invoiceReady']: {$set: nextProps.invoiceReady}
                });
        }
    }

 handleChange(event) {
        const state = update(
            this.state, {
                invoice: {
                    [event.target.id]: {$set: event.target.value}
                }
            }
        );
        this.setState(state);
    }

componentDidMount() {
        this.requestInvoice();
    }

componentDidUpdate(prevProps, prevState) {
        if (!this.state.invoiceReady) {
            this.requestInvoice();
        }
    }

我的JSX代码包含可编辑字段,如:

                        <input
                            type="text"
                            className="form-control"
                            id="invoiceDate"
                            value={this.state.invoice.invoiceDate}
                            onChange={this.handleChange}
                        />

所以 - 根据提到的模式,我通过requestInvoice向服务器发起请求,其他一些组件处理此操作并接收并将发票保存到Redux存储中,这就是为什么这些数据会自动写入的原因进入props.invoice变量,我正在props.invoice进一步将this.state.invoice写入getDerivedStateFromProps,以进一步本地处理发票数据。在使用表单的工作期间,handleChange事件被引发,并且在immutability-helper(更新函数)的帮助下,更新的字段被写入新状态并且setState被调用。

我的问题是,在setState调用期间,React正在调用getDerivedStateFromProps,因此props.invoice会覆盖来自用户的所有更改并将其处理为handleChange:setState功能。

所以 - 如何解决这个问题:setStategetDerivedStateFromProps之间的冲突?

可能有以下几种选择:

  • 我的设计可能存在缺陷。例如。也许我不应该试着将props.invoice移到this.state.invoice(为什么不呢?)?但另一方面,最好将发票保存到this.state中,并确保在编辑发票期间将所有更改应用于this.state.invoice
  • mybe我应该阻止在getDerivedStateFromProps期间执行setState

也许其他一些设计模式或代码更适合我的ReactJS表单?

我正在使用ReactJ 16.x

2 个答案:

答案 0 :(得分:1)

如果您决定使用react-redux for component state management,那么您应该avoid使用component internal state来管理setStateredux store.控制的handleChange

否则会导致此类问题以及许多if-else,您可能需要处理它。

考虑你的情景, 在setState而不是致电dispatch期间,最好是action update the state in redux storeconnect,然后通过handleChange(event) { const stateData = invoice: { [event.target.id]: {$set: event.target.value} }; this.props.callDispatch({'UPDATE_SOME_DATA',stateData}) } ,它会反映到您的组件中你调用API的方式。

reducers

只有事情需要注意,确保dispatch action仅更新所需的已通过属性数据,并保持API [ {"name":"Adele", "href":"#"}, {"name":"Agnes","href":"#"}, {"name":"Billy","href":"#"}, {"name":"Bob","href":"#"}, {"name":"Calvin","href":"#"}, {"name":"Christina","href":"#"}, {"name":"Cindy","href":"#"} ] 进程更新其他数据。

答案 1 :(得分:0)

我已经得到了以下解决方案 - 在this.state.stateUpdate变量的帮助下 - 我认为这是我的问题的答案(请评论如果这不是合理而且整洁的解决方案,我可以接受其他答案为好吧,如果有效):

 const mapStateToProps = state => {
        return {
            invoice: state.invoice,
            invoiceReady: state.invoiceReady,
            stateUpdate: false 
        };
    };

     static getDerivedStateFromProps(nextProps, prevState) {
            if (prevState.stateUpdate) {
              return update(
                prevState, {
                    stateUpdate: {$set: false}
                }
              )
            }
            if (nextProps.match.params.id !== prevState.id) {
                return update(
                    prevState, {
                        ['id']: {$set: nextProps.match.params.id},
                        ['invoiceReady']: {$set: false}
                    });
            } else {
                return update(
                    prevState, {
                        ['invoice']: {$set: nextProps.invoice},
                        ['invoiceReady']: {$set: nextProps.invoiceReady}
                    });
            }
        }

     handleChange(event) {
            const state = update(
                this.state, {
                    invoice: {
                        [event.target.id]: {$set: event.target.value}
                    },
                    stateUpdate: {$set: true}
                }
            );
            this.setState(state);
        }

    componentDidMount() {
            this.requestInvoice();
        }

    componentDidUpdate(prevProps, prevState) {
            if (!this.state.invoiceReady) {
                this.requestInvoice();
            }
        }