上一节(https://juejin.im/post/6867429672347500551)说了如何使用props,这一节介绍一下state的使用。
代码在:https://codesandbox.io/s/dawn-shape-bofqp?file=/src/index.js
index.js
import React from "react"; import ReactDOM from "react-dom"; import "./styles.css"; function App() { return ( <div className="App"> 父节点 <Son /> </div> ); } class Son extends React.Component { constructor() { super(); this.state = { n: 0 }; } add() { this.setState({ n: this.state.n + 1 }); } render() { return ( <div className="Son"> 子节点 n: {this.state.n} <button onClick={() => { this.add(); }} > +1 </button> <Grandson /> </div> ); } } const Grandson = (props) => { const [n, setN] = React.useState(0); return ( <div className="Grandson"> 孙子 n:{n} <button onClick={() => setN(n + 1)}>+1</button> </div> ); }; const rootElement = document.querySelector("#root"); ReactDOM.render(<App />, rootElement);上面的index.js中的Son组件,有如下代码:
add() { this.setState({ n: this.state.n + 1 }); }通过调用this.setState来对n数字就行+1操作,
这里为什么不可以直接通过 this.state.n++ 或者 this.state.n = this.state.n + 1 来改变呢?
因为React其实并没有去监听this.state.n这个属性,即使这个属性发生了变化,React也是无感知的,这里和Vue以及Angular是有区别的,
所以我们需要调用setState方法对React进行通知,使其刷新视图,
看下方代码:
add() { this.state.n += 1 this.setState(this.state); }读者可以用这段代码去实验一下,是可以使得视图刷新的,但是我们并不推荐这么做,因为React要遵循一个原则,数据不可变性,以前的对象不要再去改动它了,生成一个新的对象,如下:
add() { this.setState({n: this.state.n + 1}); }尽管上述的写法已经没有问题了,但是如果我们想在setState方法后打印log最新的值,我们一般会这么写:
add() { this.setState({n: this.state.n + 1}); console.log(this.state.n) }但是这种写法其实并不会生效,因为setState方法是异步去执行的,所以我们要换一种写法:
add() { this.setState((state) => { const n = state.n + 1 console.log(n) return {n: n} }); }我们在this.setState参数中传递了一个函数,这个函数中我们对 n 这个数字进行操作,并进行打印log,
最后返回一个新的对象用来告诉React,这么做的好处是方便我们在赋值操作的前后做一些额外处理,并且不会因为setState方法执行的异步造成一些困惑。
上面的index.js中的Grandson组件,有如下代码:
const [n, setN] = React.useState(0);React.useState 返回两个值,一个是 n 本身的值也就是方法中的 0,另外一个是setN方法,用来改变 n 的值,
所以在函数组件中通过 React.useState 获取到的第二个值,来操作一个属性的值就好。
// 这里是析构赋值,React.useState(0)返回了一个数组,第一个是值本身,第二个是修改值的方法,接收的属性名可以自己定义 const [n, setN] = React.useState(0);注意 setN 并不是直接去修改 n 的值的,而是生成了一个新的state,遵循了数据不可变性原则,这个后面会详细来解释。
this.state.n += 1 无效?
其实 n 已经改变了,只是UI不会自动更新, 调用 setState 才会触发UI更新 (并且更新是异步的),因为 React 没有像 Vue 监听 data 一样监听 state,所以需要手动调用 setState 去通知React进行更新操作。
setState 会异步更新UI
setState之后,state不会马上改变,立刻读取 state 会失败,更推荐的方式是 setState(函数)。
this.setState(this.state)
这种方式虽然也可以改变数据,但是React希望我们不要更改旧state,所以不推荐使用,常用的代码是 setState({n:state.n + 1})
跟类组件相似,需要通过setX(新值)来更新UI。
跟类组件不同的地方,没有this,一律使用参数和变量。
一个对象对应一个虚拟DOM,当对象的属性改变时,把属性相关的DOM节点全部更新。
PS(Vue为了其他考量,也引入了虚拟DOM和DOM diff)。
例如将 n 放在模板中,后续对 n 做直接更改,那么视图中的 n 也跟着做相应的更改。
一个对象,对应一个虚拟DOM。
另一个对象,对应另一个虚拟DOM。
对比两个虚拟DOM,找出不同之处(DOM Diff) 最后局部更新DOM。
例如刚开始创建了一个 n 对象,放到视图中,后面要新创建一个 n 对象,再放到视图中,React进行对比,做出相应的改变。