【问题标题】:Switching from componentWillReceiveProps to getDerivedStateFromProps in React在 React 中从 componentWillReceiveProps 切换到 getDerivedStateFromProps
【发布时间】:2021-06-13 21:45:51
【问题描述】:

我正在按照教程创建一个带有 .net 核心后端的 React 应用程序。我目前正在尝试设置一个组件来编辑/更新我的实体。本教程使用现已弃用的 componentWillReceiveProps 来提取正在更新的实体的数据。我正在尝试将其更新为 getDerivedStateFromProps。我已经设法让它填充输入字段,但现在我无法在提交之前实际编辑这些字段。我认为这可能与我的 if 语句有关。

这是我的组件的完整代码:

class UpdateBusiness extends Component {
    state = {
        businessForm: {},
        prevPropsData: this.props.data,
        isFormValid: true
    }

    componentDidMount = () => {
        this.setState({ businessForm: returnInputConfiguration() });
        let id = this.props.match.params.id;
        let url = '/api/business/' + id + '/account';
        this.props.onGetBusinessById(url, { ...this.props });
    }

    static getDerivedStateFromProps(props, state) {
        
        const updatedBusinessForm = state.businessForm;
        if (props.data !== state.prevPropsData)
        {
            updatedBusinessForm.businessName.value = props.data.businessName;
            updatedBusinessForm.businessWebsite.value = props.data.businessWebsite;
            updatedBusinessForm.contractStartDate.value = moment(props.data.contractStartDate).toDate()
            return {
                businessForm: updatedBusinessForm
            };
        }
        return null;
    }

    handleChangeEvent = (event, id) => {
        const updatedBusinessForm = { ...this.state.businessForm };
        updatedBusinessForm[id] = formUtilityActions.executeValidationAndReturnFormElement(event, updatedBusinessForm, id);

        const counter = formUtilityActions.countInvalidElements(updatedBusinessForm);

        this.setState({ businessForm: updatedBusinessForm, isFormValid: counter === 0 })
    }

    redirectToBusinessList = () => {
        this.props.history.push('/business-list');
    }

    updateBusiness = (event) => {
        event.preventDefault();

        const businessToUpdate = {
            businessName: this.state.businessForm.businessName.value,
            contractStartDate: this.state.businessForm.contractStartDate.value,
            businessWebsite: this.state.businessForm.businessWebsite.value
        }

        const url = "/api/business/" + this.props.data.businessId;

        this.props.onUpdateBusiness(url, businessToUpdate, { ...this.props });
    }

    render() {
        const formElementsArray = formUtilityActions.convertStateToArrayOfFormObjects({ ...this.state.businessForm });

        return (
            <Card>
                <Form horizontal onSubmit={this.updateBusiness}>
                    {
                        formElementsArray.map(element => {
                            return <Input key={element.id} elementType={element.config.element}
                                id={element.id} label={element.config.label}
                                type={element.config.type} value={element.config.value}
                                changed={(event) => this.handleChangeEvent(event, element.id)}
                                errorMessage={element.config.errorMessage} invalid={!element.config.valid}
                                shouldValidate={element.config.validation}
                                touched={element.config.touched}
                                blur={(event) => this.handleChangeEvent(event, element.id)} />
                        })
                    }
                    <br />
                    <FormGroup>
                        <Col mdOffset={6} md={1}>
                            <Button type='submit' bsStyle='info' disabled={!this.state.isFormValid}>Update</Button>
                        </Col>
                        <Col md={1}>
                            <Button bsStyle='danger' onClick={this.redirectToBusinessList}>Cancel</Button>
                        </Col>
                    </FormGroup>
                </Form>
                <SuccessModal show={this.props.showSuccessModal} modalHeaderText={'Success message'}
                    modalBodyText={'Action completed successfully'}
                    okButtonText={'OK'}
                    successClick={() => this.props.onCloseSuccessModal('/business-list', { ...this.props })} />
                <ErrorModal show={this.props.showErrorModal} modalHeaderText={'Error message'}
                    modalBodyText={this.props.errorMessage}
                    okButtonText={'OK'}
                    closeModal={() => this.props.onCloseErrorModal()} />
            </Card>
        )
    }
}

const mapStateToProps = (state) => {
    return {
        data: state.repository.data,
        showSuccessModal: state.repository.showSuccessModal,
        showErrorModal: state.errorHandler.showErrorModal,
        errorMessage: state.errorHandler.errorMessage
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        onGetBusinessById: (url, props) => dispatch(repositoryActions.getData(url, props)),
        onUpdateBusiness: (url, business, props) => dispatch(repositoryActions.putData(url, business, props)),
        onCloseSuccessModal: (url, props) => dispatch(repositoryActions.closeSuccessModal(props, url)),
        onCloseErrorModal: () => dispatch(errorHandlerActions.closeErrorModal())

    }
}

这是教程中的 componentWillReceiveProps 代码:

componentWillReceiveProps = (nextProps) => {
        const updatedBusinessForm = { ...this.state.businessForm };
        let nameObject = { ...updatedBusinessForm.businessName };
        let websiteObject = { ...updatedBusinessForm.businessWebsite };
        let dateObject = { ...updatedBusinessForm.contractStartDate };
        nameObject.value = nextProps.data.businessName;
        nameObject.valid = true;
        dateObject.value = moment(nextProps.data.contractStartDate).toDate();
        websiteObject.value = nextProps.data.businessWebsite;
        websiteObject.valid = true;
        updatedBusinessForm['businessName'] = nameObject;
        updatedBusinessForm['contractStartDate'] = dateObject;
        updatedBusinessForm['businessWebsite'] = websiteObject;
        this.setState({ businessForm: updatedBusinessForm });
}

如果有人能指出我正确的方向,我将不胜感激。我考虑过使用 componentDidUpdate,但我认为当我更新状态时,这会是错误的钩子吗?

【问题讨论】:

  • 你是在启动项目还是遗留代码?
  • 如果 props 和 state 相等,您将返回 null - 这可能应该是 return state(即您不希望状态更改)。
  • @SebastiánEspinosa 我不确定你的问题是什么意思,但我正在使用教程来开始我自己的项目。
  • @gerrod 我已经尝试过了,但不幸的是它似乎没有什么不同。我遇到的问题是我的 getDerivedStateFromProps 中的代码不允许我更新输入字段。它将每个输入字段的值设置为从数据库中提取的相应值,这很好,但它不允许我编辑这些值。

标签: reactjs .net asp.net-core


【解决方案1】:

您的getDerivedStateFromProps 方法会在每次渲染时覆盖您的表单值。更新表单时,您没有更新 prevPropsData 属性 - 您必须将返回更改为 -

return {
    businessForm: updatedBusinessForm,
    prevPropsData: props.data,
};

但总的来说 - 从 props 创建状态有点反模式,因为它很难做到正确(正是由于你在这里遇到的原因类型)。更好的选择是不在表单上保留本地状态,而是将状态提升到首先提供道具的父控件。这是最好的选择,因为它意味着表单的状态只存储在一个地方(父控件)。 Here's a small demo using functional components:

import React, { useState } from 'react';

const FormControls = (props) => {
  const handleFieldChange = fieldName => event =>
    props.onFormValueChange(fieldName, event.target.value);

  return (
    <form>
      <div>
        <label for="name">Name</label>
        <input type="text" id="name" 
          value={props.values.name}
          onChange={handleFieldChange('name')} 
        />
      </div>

      <div>
        <label for="age">Age</label>
        <input type="text" id="age"
          value={props.values.age}
          onChange={handleFieldChange('age')}
        />
      </div>
    </form>
  );
}

const FormHost = () => {
  const [form, setForm] = useState({
    name: '',
    age: 0,
  });

  const handleFormValueChange = (fieldName, nextValue) => {
    setForm(prev => ({
      ...prev,
      [fieldName]: nextValue,
    }));
  }

  return (
    <>
      <FormControls
        values={form}
        onFormValueChange={handleFormValueChange}
      />
    </>
  );
};

【讨论】:

    猜你喜欢
    • 2020-02-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-19
    • 2020-06-08
    • 1970-01-01
    • 1970-01-01
    • 2018-09-10
    相关资源
    最近更新 更多