react笔记四:你可能不需要使用派生 state(解读--个人)

避免将 props 的值复制给 state!这是一个常见的错误

通过下文,以了解出现 state 依赖 props 的情况该如何处理。

什么是派送state

个人理解:组件将接收到的props属性作为state的数据

eg:
import React, { Component } from 'react'
//子组件
class Child extends Component {
    constructor(props) {
        super(props)
        this.state = {
            title: this.props.title  //将接收到的props的属性作为state
        }
    }
    render() {
        return (
            <div>
                {this.state.title}
            </div>
        )
    }
}
//父组件
export default class Parent extends Component {
    render() {
        return (
            <div>
                <Child title="我是props派生的数据"></Child>    //1)title是组件Parent传递给子组件Child的属性
            </div>
        )
    }
}

什么时候使用派生state

  • 第一种情况:直接复制 props 到 state 上。
   this.state = {
        title: this.props.title  //将接收到的props的属性作为state
   }
  • 第二种情况:如果 props 和 state 不一致就更新 state
 componentWillReceiveProps(nextProps) {
	 if(nextProps.title!==this.props.title){
    		 this.setState({ value: nextProps.title});
        }
    }

第一种情况:直接复制 props 到 state 上

最常见的误解就是 getDerivedStateFromPropscomponentWillReceiveProps 只会在 props “改变”时才会调用。实际上只要父级重新渲染时,这两个生命周期函数就会重新调用,不管 props 有没有“变化”.

总结:props变化跟父组件重新渲染(不管 props 有没有“变化”), getDerivedStateFromPropscomponentWillReceiveProps 都会重新执行)。

造成的问题:父组件重新渲染,子组件中我们输入的所有东西都会丢失。

官方例子:

import React, { Fragment, Component } from "react";
import { render } from "react-dom";
//子组件
class EmailInput extends Component {
  state = {
    email: this.props.email
  };
  render() {
    return <input onChange={this.handleChange} value={this.state.email} />;
  }
  handleChange = event => {
    this.setState({ email: event.target.value });
  };
  componentWillReceiveProps(nextProps) {  //父组件更新会执行这个方法,让子组件通过handleChange 方法保存的Email 丢失
    this.setState({ email: nextProps.email });
  }
}

//父组件,通过setInterval实现一秒更新一次父组件
class Timer extends Component {
  state = {
    count: 0
  };
  componentDidMount() {
    this.interval = setInterval(
      () =>
        this.setState(prevState => ({
          count: prevState.count + 1
        })),
      1000
    );
  }
  componentWillUnmount() {
    clearInterval(this.interval);
  }

  render() {
    return (
      <Fragment>
        <EmailInput email="example@google.com" />
      </Fragment>
    );
  }
}

render(<Timer />, document.getElementById("root"));

第二种情况:在props变化之后更新state

在这里插入图片描述
这里出现的问题是:index.js的数据中,id=1跟id=2列表项中的Email是一样的,修改了id=1列表项中的Email值,之后切换到id=2,但是Email 并没有恢复成初始值,原因是传给UncontrolledEmaillnput.js的email没有发生变化。

组件1:index.js
const fakeAccounts = [
  {
    id: 1,
    name: "One",
    email: "fake.email@example.com",
    password: "totally fake"
  },
  {
    id: 2,
    name: "Two",
    email: "fake.email@example.com",
    password: "also fake"
  },
  {
    id: 3,
    name: "Three",
    email: "also.fake.email@example.com",
    password: "definitely fake"
  }
];
render(
  <AccountsList accounts={fakeAccounts} />,
  document.getElementById("root")
);
组件2:AccountsList.js
export default class AccountsList extends Component {
  state = {
    selectedIndex: 0
  };

  render() {
    const { accounts } = this.props;
    const { selectedIndex } = this.state;
    return (
      <Fragment>
        <EditAccountForm account={accounts[selectedIndex]} />  //当前选择列表项的数据
        <p>
          Accounts:
          {this.props.accounts.map((account, index) => (   //渲染列表数据
            <label key={account.id}>
              <input
                type="radio"
                name="account"
                checked={selectedIndex === index}
                onChange={() => this.setState({ selectedIndex: index })}   //切换列表,重新渲染,将新的列表项数据传递给组件3
              />{" "}
              {account.name}
            </label>
          ))}
        </p>
      </Fragment>
    );
  }
}
组件3:EditAccountForm.js
export default class EditAccountForm extends Component {
  render() {
    const { account } = this.props;
    return (
      <form>
        <h2>Account "{account.name}"</h2>
        <UncontrolledEmailInput email={account.email} />   //将列表项的email传递给组件4
      </form>
    );
  }
}
组件4:UncontrolledEmailInput.js
export default class UncontrolledEmailInput extends Component {
  state = {
    email: this.props.email
  };
//判断接收到的Email是否有变化,有变化的话重置Email初始值。修改了第一个列表项的Email之后,切换到第二个列表项,Email没有发生变化,原因是传入的props.email 一样,下面的代码并没有执行
  componentWillReceiveProps(nextProps) {
    if (nextProps.email !== this.props.email) {  
      this.setState({ email: nextProps.email });   //代码1
    }
  }

  handleChange = event => {
    this.setState({ email: event.target.value });
  };
  render() {
    return (
      <label>
        Email: <input onChange={this.handleChange} value={this.state.email} />
      </label>
    );
  }
}





解决办法(任何数据,都要保证只有一个数据来源,而且避免直接复制它)

1.完全可控的组件

将子组件中的state删除,使用父组件传递的props数据控制子组件状态的改变,让子组件完全受控于父组件,可以将class子组件转换成 function函数组件。

function EmailInput(props) {
  return <input onChange={props.onChange} value={props.email} />;
}

2.有 key 的非可控组件

state接收父组件props传递的数据,但是数据的改变不受父组件的控制。

1)可以使用 key 这个特殊的 React 属性。当 key 变化时, React 会创建一个新的而不是更新一个既有的组件.
2)这听起来很慢,但是这点的性能是可以忽略的。如果在组件树的更新上有很重的逻辑,这样反而会更快,因为省略了子组件 diff。

class EmailInput extends Component {
  state = { email: this.props.defaultEmail };

  handleChange = event => {
    this.setState({ email: event.target.value });
  };

  render() {
    return <input onChange={this.handleChange} value={this.state.email} />;
  }
}

3.添加key失效的解决办法

如果某些情况下 key 不起作用(可能是组件初始化的开销太大)

解决办法是:可以在渲染的时候,给组件一个唯一的属性,例如:id,title,msg,err等都可以,只要能区分开就好。

  static getDerivedStateFromProps(props, state) {
    if (props.uID !== state.uID) {
      return {
        uID: props.uID,
        email: props.defaultEmail
      };
    }
    return null;
  }
}

4.使用ref来重置组件

refs 在某些情况下很有用,比如这个。但通常我们建议谨慎使用。即使是做一个演示,这个命令式的方法也是非理想的,因为这会导致两次而不是一次渲染。

在这里插入图片描述

eg:
 handleChange = index => {
    this.setState({ selectedIndex: index }, () => {
      const selectedAccount = this.props.accounts[index];
      this.inputRef.current.resetEmailForNewUser(selectedAccount.email);  //resetEmailForNewUser是子组件的方法
    });
  };
  
  <UncontrolledEmailInput   defaultEmail={selectedAccount.email}   ref={this.inputRef} />
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值