《React 内部数据state》

711 阅读2分钟

React的内部数据用state表示,类似于Vue里的data

对于一个内部数据,我们只关注三件事:如何初始化,如何读,如何写

类组件

class Son extends React.Component {
  constructor() {
    super();
    this.state = {
      n: 0
    };
  }
  add() {
    // this.state.n += 1 为什么不行
    this.setState({ n: this.state.n + 1 });
  }
  render() {
    return (
      <div className="Son">
        儿子 n: {this.state.n}
        <button onClick={() => this.add()}>+1</button>
        <Grandson />
      </div>
    );
  }
}

初始化

内部数据的初始化写在constructor里边,super()后边。声明一个this.state对象

读数据

this.state.n 就可以直接读数据。类似于类组件里读propsthis.props.xxx

写数据 : 最好使用this.setState(函数)

this.setState({ n:this.state.n +1})

在这里我们先调用一个add函数,在add函数里去修改。

首先明确,不能直接像这样修改this.state.n += 1。因为React里是没有数据监听和劫持的,只是单纯改变数据,React并不知道,不会更新UI。虽然这时n已经变了。

应该调用this.setState()函数。

参数是一个新的对象this.setState({ n:this.state.n +1})新对象的属性n的值是旧的+1。 注意,这里并没有修改原来的对象,也就是说,没有改this.state.n。而是生成一个新的对象。这叫数据不可变

setState异步更新UI:但是直接给它传一个新对象有时候会有bug。如我想打印出加1的值。

add() {
    this.setState({ n: this.state.n + 1 });
    console.log(this.state.n)
  }

原来n=0,点击按钮+1,然后打印。但是我们发现,打印出来的还是0.

因为setState是异步的,当我们把一个新对象传给它的时候,告诉它我要把{n:0}改成{n:1},它不会马上更新state,而是等一会再去更新。等到什么时候呢?等到它的下一行执行完再去更新。所以立马读state会失败,读到的是旧state.所以打印的时候,state还是旧state。setState还没有更新。

给setState传一个函数: 为了避免混淆新旧state的问题,最好使用函数。

add() {
    this.setState((state)=>{
      return {n:state.n+1}
    })
}

给this.setState传一个函数。这个函数的参数是旧state。return 一个新对象。看起来和直接传一个新对象一样。但是写成函数就可以明显的区分旧state和新state了。如果我想打印出新的值,就可以这样写:

add() {
    this.setState((state)=>{
        const n=state.n+1
        console.log(n)
        return {n:n}
    })
}

函数组件

const Grandson = () => {
  const [n, setN] = React.useState(0);
  return (
    <div className="Grandson">
      孙子 n:{n}
      <button onClick={() => setN(n + 1)}>+1</button>
    </div>
  );
};

初始化

const [n, setN] = React.useState(0);把初始值放在React.useState()里,它会返回一个数组,数组的第一项用来读,第二项用来写。没有第三项。

读数据

{n}

写数据

setN(n + 1)

注意

类组件里的setState不会马上改变state,等一会再改变。

函数组件里的setN()永远不会改变n,而是生成一个新的n

函数组件里没有this