【问题标题】:How to refactor componentWillReceiveProps to componentDidUpdate or possibly a Hook?如何将 componentWillReceiveProps 重构为 componentDidUpdate 或可能的 Hook?
【发布时间】:2020-01-28 17:15:17
【问题描述】:

我的这个应用程序有一个已弃用的生命周期方法:

componentWillReceiveProps(nextProps) {
    if (this.state.displayErrors) {
      this._validate(nextProps);
    }
  }

目前,我使用了UNSAFE_ 标志:

UNSAFE_componentWillReceiveProps(nextProps) {
    if (this.state.displayErrors) {
      this._validate(nextProps);
    }
  }

我把它留成这样是因为当我试图将它重构为:

componentDidUpdate(prevProps, prevState) {
     if (this.state.displayErrors) {
       this._validate(prevProps, prevState);
     }
   }

它创建了另一个给我这个错误的错误:

不变违规:超过最大更新深度。这可能发生 当组件在 componentWillUpdate 中重复调用 setState 或 componentDidUpdate。 React 将嵌套更新的数量限制为 防止无限循环。

当用户单击“立即付款”按钮启动 _handlePayButtonPress 时,它开始发生,它还会检查信用卡信息的验证,如下所示:

UNSAFE_componentWillReceiveProps(nextProps) {
    if (this.state.displayErrors) {
      this._validate(nextProps);
    }
  }

  _validate = props => {
    const { cardExpireDate, cardNumber, csv, nameOnCard } = props;

    const validationErrors = {
      date: cardExpireDate.trim() ? "" : "Is Required",
      cardNumber: cardNumber.trim() ? "" : "Is Required",
      csv: csv.trim() ? "" : "Is Required",
      name: nameOnCard.trim() ? "" : "Is Required"
    };

    if (validationErrors.csv === "" && csv.trim().length < 3) {
      validationErrors.csv = "Must be 3 or 4 digits";
    }

    const fullErrors = {
      ...validationErrors,
      ...this.props.validationErrors
    };
    const isValid = Object.keys(fullErrors).reduce((acc, curr) => {
      if (fullErrors[curr]) {
        return false;
      }

      return acc;
    }, true);

    if (isValid) {
      this.setState({ validationErrors: {} });
      //register
    } else {
      this.setState({ validationErrors, displayErrors: true });
    }

    return isValid;
  };

  _handlePayButtonPress = () => {
    const isValid = this._validate(this.props);

    if (isValid) {
      console.log("Good to go!");
    }

    if (isValid) {
      this.setState({ processingPayment: true });
      this.props
        .submitEventRegistration()
        .then(() => {
          this.setState({ processingPayment: false });
          //eslint-disable-next-line
          this.props.navigation.navigate("PaymentConfirmation");
        })
        .catch(({ title, message }) => {
          Alert.alert(
            title,
            message,
            [
              {
                text: "OK",
                onPress: () => {
                  this.setState({ processingPayment: false });
                }
              }
            ],
            {
              cancelable: false
            }
          );
        });
    } else {
      alert("Please correct the errors before continuing.");
    }
  };

不幸的是,我没有足够的 Hooks 经验,并且我未能将已弃用的生命周期方法重构为不会像上述错误那样造成麻烦的方法。对更好的 CDU 有什么建议或任何其他想法?

【问题讨论】:

    标签: reactjs react-native


    【解决方案1】:

    您需要再次检查,以免陷入无限循环(每次调用 setState 时,您都会重新渲染 -> 组件确实更新了 -> 再次更新...)

    你可以这样做:

    componentDidUpdate(prevProps, prevState) {
         if (this.state.displayErrors && prevProps !== this.props) {
           this._validate(prevProps, prevState);
         }
       }
    

    另外我认为你需要用新的道具和状态来调用你的验证:

    this._validate(this.props, this.state);
    

    希望这会有所帮助。

    【讨论】:

    • 如果我在不添加信用卡信息的情况下点击立即付款,我实际上仍然会陷入无限循环。所以我解决了添加信用卡信息时发生的无限循环,但是在没有信用卡信息的情况下提交时仍然存在无限循环。
    【解决方案2】:

    componentDidUpdate 不应该因为这个原因替换 componentWillRecieveProps。 React 给我们的替代品是 getDerivedStateFromProps,你可以在这里阅读 https://medium.com/@baphemot/understanding-react-react-16-3-component-life-cycle-23129bc7a705。但是,getDerivedStateFromProps 是一个静态函数,因此您必须替换 _validate 中的所有 setState 行并返回一个对象。

    【讨论】:

    • 我记得读过有关getDerivedStateFromProps 的文章,但因为它是static 函数,所以我不太记得我是如何开始使用CDU 的。 _validate() 函数中唯一的 setState() 行是 if 条件中的内容,它似乎存在更新状态,具体取决于 isValid 是否为真。什么样的对象会取代setState 来达到更新状态的相同目的?
    • 它只是一个与状态具有所有相同字段的对象,因为 React 将合并它类似于setState。所以你仍然会使用validationErrorsdisplayErrors,只需将this.setState({ validationErrors, displayErrors: true }); 更改为return { validationErrors, displayErrors: true };
    【解决方案3】:

    这就是你使用 prevState 和钩子的方式。

    工作样本Codesandbox.io

    import React, { useState, useEffect } from "react";
    import "./styles.css";
    
    const ZeroToTen = ({ value }) => {
      const [myValue, setMyValue] = useState(0);
      const [isValid, setIsValid] = useState(true);
    
      const validate = value => {
        var result = value >= 0 && value <= 10;
        setIsValid(result);
        return result;
      };
    
      useEffect(() => {
        setMyValue(prevState => (validate(value) ? value : prevState));
      }, [value]);
    
      return (
        <>
          <span>{myValue}</span>
          <p>
            {isValid
              ? `${value} Is Valid`
              : `${value} is Invalid, last good value is ${myValue}`}
          </p>
        </>
      );
    };
    
    export default function App() {
      const [value, setValue] = useState(0);
    
      return (
        <div className="App">
          <button value={value} onClick={e => setValue(prevState => prevState - 1)}>
            Decrement
          </button>
          <button value={value} onClick={e => setValue(prevState => prevState + 1)}>
            Increment
          </button>
    
          <p>Current Value: {value}</p>
    
          <ZeroToTen value={value} />
        </div>
      );
    }
    

    我们有两个组件,一个用于增加/减少一个数字,另一个用于保存 0 到 10 之间的数字。

    第一个组件使用 prevState 来增加值,如下所示:

    onClick={e => setValue(prevState => prevState - 1)}
    

    它可以随心所欲地增加/减少。

    第二个组件从第一个组件接收输入,但它会在每次更新时验证该值,并允许 0 到 10 之间的值。

    useEffect(() => {
        setMyValue(prevState => (validate(value) ? value : prevState));
      }, [value]);
    

    在这种情况下,每次更新“值”时,我都会使用两个挂钩来触发验证。

    如果您还不熟悉钩子,这可能会让人感到困惑,但主要的想法是,使用钩子时,您需要专注于单个属性/状态来验证更改。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-02
      • 2020-10-15
      • 2018-08-29
      • 1970-01-01
      • 2017-10-24
      • 2021-08-13
      • 1970-01-01
      • 2020-10-22
      相关资源
      最近更新 更多