修改 Clock 组件的 render() 方法:
render() { + console.log(1) return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); }控制台输出:
1 ⑨ 1组件一挂载就得渲染,输出第一行的 1,然后每过一秒就会在第二行输出,这里统计到第 9 秒。
所以,render() 执行 1 + N 次。N 在这里表示状态更改了 9 次。
公有实例字段优化 state 初始化请看示例:
<script> class Dog { age = 18 constructor(name) { this.name = name } } let d = new Dog('peng') console.log('d: ', d); </script>控制台输出:d: Dog {age: 18, name: 'peng'}
age = 18 等价于给实例定义了一个属性 age。于是我们可以将通过次语法来优化 state 的初始化。
优化前:
class Clock extends React.Component { constructor() { super() this.state = { date: new Date() } setInterval(this.tick.bind(this), 1000) } }优化后:
class Clock extends React.Component { state = { date: new Date() } constructor() { super() setInterval(this.tick.bind(this), 1000) } }Tip:setInterval() 通常会移出构造函数,例如放在某生命钩子函数中,所以整个构造函数 constructor() 都可以省略
在函数组件中使用 stateHook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性 —— 官网-Hook API 索引
前面我们一直是在 class 组件中使用 state。就像这样:
class Clock extends React.Component { state = { date: new Date(), name: 'pjl' } constructor() { super() setInterval(this.tick.bind(this), 1000) } tick() { this.setState({ date: new Date() }) } render() { return ( <div> <h1>Hello, world! {this.state.name}</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } }而通过 useState Hook 能让我们在函数组件使用 state。实现上述相同的功能:
function Clock() { // 一个 state 就得调用一次 useState() const [name] = React.useState('pjl') // 解构赋值 const [date, setDate] = React.useState(new Date()) setInterval(() => { // 更新 state setDate(new Date()) }, 1000) return ( <div> <h1>Hello, world! {name}</h1> <h2>It is {date.toLocaleTimeString()}.</h2> </div> ); }一个 state 就得调用一次 useState(initialState)
React.useState 返回一个 state,以及更新 state 的函数
在初始渲染期间,返回的状态 (state) 与传入的第一个参数 (initialState) 值相同
在后续的重新渲染中,useState 返回的第一个值将始终是更新后最新的 state
Tip:请问控制台输出几次 a:
function Clock() { console.log('a') // ... 不变 }答案是:1 + N 次。Clock 函数被反复的调用,但 useState() 返回的第一个值始终是更新后最新的 state,所以能猜出 react 做了特殊处理。
事件处理 事件命名采用小驼峰式React 事件的命名采用小驼峰式(camelCase),而不是纯小写。例如在 html 中通常都是小写,就像这样:
// onclick - 小写 <button>click</button>下面这个组件,每点击一次 button,控制台就会输出一次 lj:
class EventDemo1 extends React.Component { handleClick() { console.log('lj') } render() { return ( <button onClick={this.handleClick}> click </button> ); } }Tip:使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串 —— 官网
倘若将 onClick 改成 onclick,浏览器控制台将报错如下:
Warning: Invalid event handler property `onclick`. Did you mean `onClick`? 事件中的 this假如我们在 EventDemo1 中读取状态。就像这样:
class EventDemo1 extends React.Component { state = { name: 'lj' } handleClick() { console.log(typeof this) // 读取状态 console.log(this.state.name) } render() { return ( <button onClick={this.handleClick}> click </button> ); } }控制报错:
undefined Uncaught TypeError: Cannot read properties of undefined (reading 'state')我们根据错误信息能推测出在 handleClick() 方法中没有 this。
Tip:现在有一个事实:即我们自己的方法中没有 this,而 render() 方法中却有 this。猜测 react 只帮我们处理了 render() 方法中的 this。
所以,我们需要处理一下自定义方法中的 this。请看实现:
class EventDemo1 extends React.Component { state = { name: 'lj' } handleClick = () => { console.log(typeof this) console.log(this.state.name) } render() { // ... } }每次点击 button,都会输出:
object lj处理 this 的方法有两点:
将原型中的方法移到实例上来
使用箭头函数。由于箭头函数没有 this,而将箭头函数中的 this 输出来,却正好就是实例