2022-11-24 11:15:20 +08:00

6.9 KiB
Raw Blame History

组件状态与数据传递

本章主要包含以下知识点:

  • 组件状态
  • props
  • props 验证
  • 状态提升

组件状态

早期类组件被称之为有状态组件,就是因为在类组件中能够维护组件数据。

class 类名 extends React.Component{
  constructor(){
    super();
    // 设置组件自身的数据状态
    this.state = {
      xxx : xxx
    }
  }
  render(){
    return (
    	// 通过 {this.state.xxx} 来获取状态数据
    )
  }
}

// 或者
class 类名 extends React.Component{
  state = {
      xxx : xxx
  }
  render(){
    return (
    	// 通过 {this.state.xxx} 来获取状态数据
    )
  }
}

不要直接去修改状态值,而是应该通过 setState 方法修改组件的 state 状态数据。

this.setState({
  xxx: 新值
})

setState,它对状态的改变,可能是异步的。

如果改变状态的代码处于某个 HTML 元素的事件中,则其是异步的,否则是同步

如果在事件处理函数里面想拿到 setState 执行后的数据,可以提前使用一个变量来存储计算结果,或者使用 setState 的第二个参数,它是一个函数,这个函数会在 state 更新后被调用。

最佳实践:

  1. 把所有的 setState 当作是异步的
  2. 永远不要信任 setState 调用之后的状态
  3. 如果要使用改变之后的状态,需要使用回调函数(setState 的第二个参数)
  4. 如果新的状态要根据之前的状态进行运算,使用函数的方式改变状态(setState 的第一个函数)

React 会对异步的 setState 进行优化,将多次 setState 进行合并(将多次状态改变完成后,再统一对 state 进行改变,然后触发 render

props

Vue 一样,在 React 中组件会存在层级关系,那么自然会涉及到组件之间进行数据的传递。

如果是父组件向子组件传递数据,则使用 props

如果是函数组件,props 作为函数的一个参数传入:

function 组件名(props) {
  return (
    // 一段 JSX
    // 通过 props.xxx 获取传入的值
    <div>
      <p>姓名{props.name}</p>
      <p>年龄{props.age}</p>
      <p>性别{props.gender}</p>   
    </div>
  );
}

如果是类组件,则需要在 constructor 中将 props 通过 super 传递给父类,然后通过 this.props 的方式来获取传入的值:

class 组件名 extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
       // 一段 JSX
    	 // 通过 this.props.xxx 获取传入的值
        <div>
          <p>姓名{this.props.name}</p>
          <p>年龄{this.props.age}</p>
          <p>性别{this.props.gender}</p>   
        </div>
     );
	}
}

通过 props.children,可以实现类似于 Vue 中插槽的功能,例如:

class 组件B extends React.Component{
  constructor(props){
    super(props);
  }
  render(){
    return (
      <div>
          {this.props.children}
      </div>
    );
  }
}
class 组件A extends React.Component{
  constructor(props){
    super(props);
  }
  render(){
    return (
      <组件B>
        <p>Hello, React</p>
        <p>Hello, Redux</p>
        <p>Hello, Facebook</p>
        <p>Hello, Google</p>
      </组件B>
    );
  }
}

props 验证

Vue 中,可以对传入的 props 设置默认值,以及验证 props 的有效性,在 React 中,针对 props 也可以做这些事。

通过 defaultprops 就可以对 props 设置默认值。

function Greeting(props) {
  const { name, age, gender } = props;
  return (
    <div>
      <p>姓名{name}</p>
      <p>年龄{age}</p>
      <p>性别{gender}</p>   
    </div>
   );
}
// 设置默认的 props 值,当组件没有传值时会使用默认值
Greeting.defaultProps = {
  name : 'xiejie',
  age : 18,
  gender : 'male'
};
class Greeting extends React.Component {
  constructor(props) {
    super(props);
  }
  // 设置默认的 defaultProps 属性值
  static defaultProps = {
    name : "xiejie",
    age : 18,
    gender : 'male' 
  }
  render() {
    return (
      <div>
        <p>姓名{this.props.name}</p>
        <p>姓名{this.props.age}</p>
        <p>姓名{this.props.gender}</p>
      </div>
    );
  }
}
// 或者
Greeting.defaultProps = {
    name : "xiejie",
    age : 18,
    gender : 'male' 
}

关于 props 的类型检查,从 React v15.5 版本开始,移入到了 prop-types

import PropTypes from 'prop-types';

class Greeting extends React.Component {
  render() {
    return (
      <h1>Hello, {this.props.name}</h1>
    );
  }
}

Greeting.propTypes = {
  name: PropTypes.string
};
import PropTypes from 'prop-types'

function HelloWorldComponent({ name }) {
  return (
    <div>Hello, {name}</div>
  )
}

HelloWorldComponent.propTypes = {
  name: PropTypes.string
}

export default HelloWorldComponent

状态提升

Vue 中,父传子通过 props,子传父通过触发自定义事件。

React 中,如果子组件需要向父组件传递数据,同样是通过触发父组件传递给子组件的事件来进行传递。

这在官网中被称之为“状态提升”:https://zh-hans.reactjs.org/docs/lifting-state-up.html

汇率转换案例:

父组件

import React from "react";
import Money from "./component/Money";

// 类组件
class App extends React.Component {
  state = {
    dollar: "",
    rmb: ""
  }

  transformToRMB = (value) => {
    if (parseFloat(value) || value === "" || parseFloat(value) === 0) {
      this.setState({
        dollar: value,
        rmb: value === "" ? "" : (value * 7.3255).toFixed(2)
      })
    } else {
      alert("请输入数字");
    }
  }

  transformToDollar = (value) => {
    if (parseFloat(value) || value === "" || parseFloat(value) === 0) {
      this.setState({
        dollar: value === "" ? "" : (value * 0.1365).toFixed(2),
        rmb: value
      })
    } else {
      alert("请输入数字");
    }
  }

  render() {
    return (
      <div>
        <Money text="美元" money={this.state.dollar} transform={this.transformToRMB} />
        <Money text="人民币" money={this.state.rmb} transform={this.transformToDollar} />
      </div>
    )
  }

}

export default App;

子组件

import React from 'react';

function Money(props) {


    function handleChange(e){
       // 在子组件中,要做的事情很简单,将用户数据的值,传递给父组件
       // 让父组件来进行修改
       props.transform(e.target.value);
    }


    return (
        <fieldset>
            <legend>{props.text}</legend>
            <input type="text" value={props.money} onChange={handleChange}/>
        </fieldset>
    );
}

export default Money;

-EOF-