Hook简介
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
State Hook
import React, { Component } from 'react'
function Demo(){
// 声明一个叫 "count" 的 state 变量
const [count,setCount] = React.useState(0)
function add(){
setCount(count+1) //第二种写法:setCount(count => count+1)
}
return (
<div>
<h1>当前count值为:{count}</h1>
<button onClick={add}>点我加1</button>
</div>
)
}
Effect Hook
可以把 useEffect Hook 看做
componentDidMount,componentDidUpdate和componentWillUnmount这三个函数的组合。
import React, { Component } from 'react'
function Demo(){
const [count,setCount] = React.useState(0)
//useEffect可以在函数组件中用于模拟类组件中的生命钩子
React.useEffect(()=>{
let timer = setInterval(() => {
setCount(count=>count+1) // componentDidmount componentDidUpdate
}, 1000);
return ()=>{
clearInterval(timer) //相当于在componentWillUnmount里清除定时器
}
},[count])
function add(){
setCount(count=>count+1)
}
function unmount(){
ReactDOM.unmountComponentAtNode(document.getElementById('root'))
}
return (
<div>
<h1>当前count值为:{count}</h1>
<button onClick={add}>点击加1</button>
<button onClick={unmount}>点击卸载组件</button>
</div>
)
}
useEffect(() => { return () => {} }, []),第二个参数如果指定的是[], 回调函数只会在第一次render()后执行;上述代码中[count]代表检测count,回调函数会在count每次改变后执行;如果省略第二个参数,代表检测所有变量。
Ref Hook
类似类组件中的React.createRef()
function Demo(){
const myRef = React.useRef() //创建
function show(){
//使用
alert(myRef.current.value)
}
return (
<div>
{/*绑定*/}
<input type="text" ref={myRef}/>
<button onClick={show}>点我提示数据</button>
</div>
)
}
setState更新状态的写法
import React, { Component } from 'react'
class Demo extends Component {
state = {count:0}
add = ()=>{
const {count} = this.state
//setState(stateChange, [callback])
//callback是可选函数,在状态更新和界面更新后调用
//1.对象形式
// this.setState({count:count+1},()=>{
// console.log(this.state.count)
// })
//2.函数形式
this.setState((state,props)=>{
return {count:state.count+1}
})
}
render(){
return (
<div>
<h1>当前求和为:{this.state.count}</h1>
<button onClick={this.add}>点我加1</button>
</div>
)
}
}
Fragment
React 中的一个常见模式是一个组件返回多个元素。Fragments 允许你将子列表分组,而无需向 DOM 添加额外节点。
function Demo(){
//文档碎片作用代替外层div Fragment只能有一个key属性
const {Fragment} = React
return (
<Fragment>
<h1>Fragment...</h1>
</Fragment>
)
}
Context
一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信
const Context = React.createContext()
const {Provider,Consumer} = Context
function DemoOne(){
const [name] = React.useState('sandwich')
return (
<div>
<Provider value={name}>
<DemoTwo />
</Provider>
</div>
)
}
function DemoTwo(){
//第一种方式:仅适用于类组件
//static contextType = Context 声明接收context
//this.context 读取context中的value数据
return (
<div>
{/*第二种方式,通用*/}
<Consumer>
{
value => <h3>----DemoOne传递过来的值:{value}</h3>
}
</Consumer>
</div>
)
}
组件优化
Component 的2个问题
- 只要执行
setState(),即使不改变状态数据, 组件也会重新render() - 只要当前父组件重新 render(), 就会自动重新 render 子组件,效率低
原因:Component 中的shouldComponentUpdate()总是返回true
解决办法:
- 重写shouldComponentUpdate()方法,比较新旧 state 或 props 数据, 如果有变化才返回true, 如果没有返回 false
- 使用 PureComponent,PureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回 true
class Parent extends React.PureComponent {
state = {count:1}
test = ()=>{
this.setState({}) //只是调用setState,并没有改变任何状态
}
render(){
console.log('parent-render') //输出一次
return (
<div>
<h1>Parent</h1>
<button onClick={this.test}>点击按钮</button>
<Child/>
</div>
)
}
}
class Child extends React.PureComponent {
render(){
console.log('child-render') //输出一次
return (
<div>Child</div>
)
}
}
Render Props
具有 render prop 的组件接受一个返回 React 元素的函数,并在组件内部通过调用此函数来实现自己的渲染逻辑。
// 类似 Vue 中的组件插槽
class Parent extends React.Component {
render(){
return (
<div>
<A render={data=><B data={data}></B>}></A>
</div>
)
}
}
class A extends React.Component {
state={test:'A需要给B传递的数据'}
render(){
const {test} = this.state
return(
<div>
{/*调用传递过来的render函数展示B组件,并传递数据(实参)*/}
{this.props.render(test)}
</div>
)
}
}
class B extends React.Component{
render(){
return (
<div>
{/*接收A传递过来的数据*/}
<h3>{this.props.data}</h3>
</div>
)
}
}
错误边界
用来捕获后代组件错误,渲染出备用页面 使用方式:
getDerivedStateFromError配合componentDidCatch
特点:使用错误边界,当后代组件只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误。
class Parent extends React.Component {
state={
hasError:'' //用于标识子组件是否产生错误
}
//Parent的子组件出现错误时便会调用这个钩子
static getDerivedStateFromError(error){
console.log('子组件出错了')
return {hasError:error}
}
componentDidCatch(){
console.log('渲染组件时出错,通知编码人员出了bug')
}
render(){
return (
<div>
{this.state.hasError ? <h2>当前网络不稳定,请稍后再试</h2> : <Child/>}
</div>
)
}
}
class Child extends React.Component {
state = { users:''}
render(){
const {users} = this.state
return (
<div>
{
users.map((item)=>{
return <li key={item.id}>{item.id}---{item.name}</li>
})
}
</div>
)
}
}
上述代码 users是空字符,Child中遍历 users 便会出错,使用错误边界后不会使整个页面崩溃。