【问题标题】:How to replace componentWillRecieveProps如何替换 componentWillRecieveProps
【发布时间】:2019-03-23 22:47:59
【问题描述】:

我是 redux 的新手,跟随 this tutorial 创建了一个简单的带有 react 和 redux 的博客应用程序。我已经完成了,但是我注意到componentWillRecievePropsbeing deprecated。我正在尝试用更新的代码替换它,但一直无法理解如何做到这一点。我已经阅读了this article 关于用“getDerivedStateFromProps”替换“componentWillReceiveProps”的内容,但我不认为这是getDerivedStateFromProps 的正确用法,因为React's blog post 将其中一个替换为另一个描述。

我当前的componentWillRecieveProps 代码:

import axios from "axios";
import React from "react";
import { connect } from "react-redux";

class Form extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            title: "",
            body: "",
            author: ""
        };

        this.handleChangeField = this.handleChangeField.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
    }

    //This is deprecated
    componentWillReceiveProps(nextProps) {
        console.log(this);
        if (nextProps.articleToEdit) {
            this.setState({
                title: nextProps.articleToEdit.title,
                body: nextProps.articleToEdit.body,
                author: nextProps.articleToEdit.author
            });
        }
    }


    handleSubmit() {
        const { onSubmit, articleToEdit, onEdit } = this.props;
        const { title, body, author } = this.state;

        if (!articleToEdit) {
            return axios
                .post("http://localhost:8000/api/articles", {
                    title,
                    body,
                    author
                })
                .then(res => onSubmit(res.data))
                .then(() => this.setState({ title: "", body: "", author: "" }));
        } else {
            return axios
                .patch(
                    `http://localhost:8000/api/articles/${articleToEdit._id}`,
                    {
                        title,
                        body,
                        author
                    }
                )
                .then(res => onEdit(res.data))
                .then(() => this.setState({ title: "", body: "", author: "" }));
        }
    }

    handleChangeField(key, event) {
        this.setState({
            [key]: event.target.value
        });
    }

    render() {
        const { articleToEdit } = this.props;
        const { title, body, author } = this.state;

        return (
            <div className="col-12 col-lg-6 offset-lg-3">
                <input
                    onChange={ev => this.handleChangeField("title", ev)}
                    value={title}
                    className="form-control my-3"
                    placeholder="Article Title"
                />
                <textarea
                    onChange={ev => this.handleChangeField("body", ev)}
                    className="form-control my-3"
                    placeholder="Article Body"
                    value={body}
                />
                <input
                    onChange={ev => this.handleChangeField("author", ev)}
                    value={author}
                    className="form-control my-3"
                    placeholder="Article Author"
                />
                <button
                    onClick={this.handleSubmit}
                    className="btn btn-primary float-right"
                >
                    {articleToEdit ? "Update" : "Submit"}
                </button>
            </div>
        );
    }
}

const mapDispatchToProps = dispatch => ({
    onSubmit: data => dispatch({ type: "SUBMIT_ARTICLE", data }),
    onEdit: data => dispatch({ type: "EDIT_ARTICLE", data })
});

const mapStateToProps = state => ({
    articleToEdit: state.home.articleToEdit
});

export default connect(
    mapStateToProps,
    mapDispatchToProps
)(Form);

Full Repo Codesandbox

如果 componentWillRecieveProps 被弃用,我应该如何更新我的代码?

编辑:使用getDerivedStateFromProps,nextProps 仍然具有以前的值,所以当返回对象时,状态被设置回以前的状态,实际上没有更新。

在这种情况下尝试使用 prevState 值是行不通的,因为初始 prevState 值都是空字符串,在组件呈现时(在初始页面加载时以及单击编辑按钮时发生)都会调用这些空字符串并将其置入状态。

    static getDerivedStateFromProps(nextProps, prevState) {
    if (
        JSON.stringify(nextProps.articleToEdit) !==
        JSON.stringify(prevState.articleToEdit)
    ) {
        console.log(nextProps);
        console.log(prevState);
        return {
            title: nextProps.articleToEdit.title,
            body: nextProps.articleToEdit.body,
            author: nextProps.articleToEdit.author
        }; // <- is this actually equivalent to this.setState()?
    } else {
        return null;
    }
}

【问题讨论】:

标签: reactjs redux


【解决方案1】:

componentWillReceiveProps() 方法已被弃用,它引入了一个名为 getDerivedStateFromProps() 的新生命周期方法。

请记住,您应该始终将当前的 props 与以前的 props 进行比较,如果它们不一样,则执行 setState 否则您将进入无限的 setState 警告

将下面的代码替换为 componentWillReceiveProps 方法

   static getDerivedStateFromProps(nextProps,  prevState)  {
          //Below I used JSON.stringify to compare current props with previous props of articleToEdit object
          if (JSON.stringify(nextProps.articleToEdit) !== JSON.stringify(prevState.artileToEdit)){
        return({
            title: nextProps.articleToEdit.title,
            body: nextProps.articleToEdit.body,
            author: nextProps.articleToEdit.author
        });// <- this is setState equivalent
       }
       return null;
   }

编辑:

您无法在 getDerivedStateFromProps 函数中访问 this.props。如果你想在函数内部访问一些以前的道具(比如比较),那么你可以在状态中镜像这些值。然后您可以将 nextProps 与存储在上述状态中的值进行比较

查看此主题以获得更好的理解

https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#updating-state-based-on-props

【讨论】:

  • 你不能在getDerivedStateFromProps里面使用this.setState,只需要返回对象来设置状态。
  • 感谢指正。我没注意到。
  • 根据文档,如果不满足条件,您可能需要return null
  • @Taylor 对此感到抱歉。我更新了答案,请看一下
  • @Think 有趣的阅读。似乎这只是一个自动的setState 来利用constructor,不仅根据道具设置初始状态,而且还处理道具更新。测试测试....
【解决方案2】:

我的建议是注意 React 给我们的警告,因为它被滥用而替换这个生命周期方法——他们告诉我们只有在你真的必须这样做时才这样做,因为可能有更好的方法(包括较新的getDerivedStateFromProps)。

根据我在这段代码中收集的信息,您正在渲染一个表单,让用户与之交互,但在某些情况下,您将用户的输入替换为道具(覆盖组件状态)。很容易看出为什么这是一种反模式,但在不了解应用程序的情况下重新架构很棘手。

我建议将输入的值移动到父状态(或创建一个新的父状态),并更改为value={props.title} 等。如果在任何情况下都存在控制表单状态的父级,您可以一直保持表单状态。

【讨论】:

    【解决方案3】:

    好的。这是您的应用程序的工作示例。我完全重写了它的结构,以便每个组件处理自己的一组状态和道具。这些组件也是可重复使用的,因此它们可以放置在任何地方——只要它有所需的道具。

    注意事项:

    1. 不要混用 .jsx.js(请参阅步骤 6 进行修复)
    2. 分隔您的container-components from your components。还可以制作可重用的组件——这极大地简化了您的状态和道具逻辑。
    3. 对于您的应用程序,您可以完全删除 redux,而让您的服务器成为“事实来源”。因此,无需保存到 redux 状态并操作 redux 状态,您只需发出 AJAX 请求以利用您的 APICRUD 发送您的 articles(例如,表单提交会将 post 请求发送到 @987654331 @,它将向数据库添加一篇文章,然后将用户重定向回/articles,或者它可以在发布后简单地返回所有文章并使用新数据更新ShowArticles状态)。由您决定如何处理。但是,话虽如此,您真正需要 redux 的唯一情况是,如果您要共享来自两个独立的重度嵌套组件的 props。
    4. 如果使用 redux,请添加 typesactions 文件夹,如下例所示。它使您的代码更易于阅读且更模块化。
    5. 避免使用index.js,除非根文件夹只包含一个文件。否则,当应用程序中断时(它会中断),如果有很多“index.js”,则很难确定哪个“index.js”中断。
    6. 您使用的样板已经很老了,使用它会让您的生活变得更加艰难。我创建了一个MERN Fullstack Boilerplate,它允许以下内容:类方法中的胖箭头函数、scss/css node_module 导入、组件级 scss 模块导入、同时运行serverclient,有一个错误覆盖,eslinting,还有更多。我强烈建议将你拥有的东西移植到它上面。

    工作示例:https://codesandbox.io/s/4x4kxn9qxw(不幸的是,create-react-app 模板不允许 scss 模块导入......哦,好吧,你会明白的)

    【讨论】:

    • 哇,谢谢你!我已经将我的输入分离到它们自己的受控组件中,但我肯定会使用它。非常感谢您花时间和精力帮助我学习。这个项目的重点是学习 redux,所以我可能仍会保留它,但我将添加一个 types 和 actions 文件夹。谢谢!
    • 绝对!如果您需要帮助,请不要犹豫。编码愉快!
    猜你喜欢
    • 1970-01-01
    • 2018-01-13
    • 1970-01-01
    • 1970-01-01
    • 2020-12-30
    • 1970-01-01
    • 2019-12-31
    • 1970-01-01
    • 2014-12-17
    相关资源
    最近更新 更多