20190313162354.png
Hooks
出了有段时间了,不知盆友们有在项目中开始使用了吗❓如果还没了解的童鞋,可以瞧瞧这篇文章,对比看下三大基础 Hooks
和传统 class
组件的区别和用法吧?
我们所指的三个基础 Hooks
是:
useState
在函数式组件内维护 state
useEffect
函数式组件内有副作用的调用与 componentDidMount
、componentDidUpdate
类似但又有所区别useContext
监听 provider 更新变化useState
允许我们在函数式组件中维护 state
,传统的做法需要使用类组件。举个例子?,我们需要一个输入框,随着输入框内容的改变,组件内部的 label
标签显示的内容也同时改变。下面是两种不同的写法:
不使用 useState:
1import React from "react";
2// 1
3export class ClassTest extends React.Component {
4 // 2
5 state = {
6 username: this.props.initialState
7 }
8 // 3
9 changeUserName(val) {
10 this.setState({ username: val })
11 }
12 // 4
13 render() {
14 return (
15 <div>
16 <label style={{ display: 'block' }} htmlFor="username">username: {this.state.username}</label>
17 <input type="text" name="username" onChange={e => this.changeUserName(e.target.value)} />
18 </div>
19 )
20 }
21}
22
React.Component
处继承state
state
的方法render
函数返回 JSX
✅使用 useState:
1// 1
2import React, { useState } from "react";
3
4export function UseStateTest({ initialState }) {
5 // 2
6 let [username, changeUserName] = useState(initialState)
7 // 3
8 return (
9 <div>
10 <label style={{ display: 'block' }} htmlFor="username">username: {username}</label>
11 <input type="text" name="username" onChange={e => changeUserName(e.target.value)} />
12 </div>
13 )
14}
15
在父组件中使用:
1import React from "react";
2// 引入组件
3import { UseStateTest } from './components/UseStateTest'
4
5// 4
6const App = () => (
7 <div>
8 <UseStateTest initialState={'initial value'} />
9 </div>
10)
11
12export default App;
13
react
中引入 useState
这个?useState
方法,接收一个初始化参数,定义 state
变量,以及改变 state
的方法state
这个变量即可,增加事件处理函数触发改变 state
的方法props
传递 initialState
初始化值用 useState
方法替换掉原有的 class
不仅性能会有所提升,而且可以看到代码量减少很多,并且不再需要使用 this
,所以能够维护 state
的函数式组件真的很好用?
class classTest extends React.Components {}
? function UseStateTest(props) {}
函数式组件写法this.state.username
? username
使用 state
不需要 this
this.setState({ username: '' })
? changeUserName('')
改变 state
也不需要书写 setState
方法文档说明:https://zh-hans.reactjs.org/docs/hooks-state.html
useEffect
是专门用来处理副作用的,获取数据、创建订阅、手动更改 DOM 等这都是副作用。你可以想象它是 componentDidMount
和 componentDidUpdate
及 componentWillUnmount
的结合。
举个例子??,比方说我们创建一个 div
标签,每当点击就会发送 http
请求并将页面 title
改为对应的数值:
1import React from 'react'
2// 1
3import { useState, useEffect } from 'react'
4
5export function UseEffectTest() {
6 let [msg, changeMsg] = useState('loading...')
7 // 2
8 async function getData(url) { // 获取 json 数据
9 return await fetch(url).then(d => d.json())
10 }
11 // 3
12 async function handleClick() { // 点击事件改变 state
13 let data = await getData('https://httpbin.org/uuid').then(d => d.uuid)
14 changeMsg(data)
15 }
16 // 4
17 useEffect(() => { // 副作用
18 document.title = msg
19 })
20 return (
21 <div onClick={() => handleClick()}>{msg}</div>
22 )
23}
react
中引入 useEffect
?getData
方法handleClick
useEffect
处理副作用:改变页面的 title
如果使用传统的类组件的写法:
1import React from 'react'
2// 1
3export class ClassTest extends React.Component {
4 // 2
5 state = {
6 msg: 'loading...'
7 }
8 // 3
9 async getData(url) { // 获取 json 数据
10 return await fetch(url).then(d => d.json())
11 }
12 handleClick = async () => { // 点击事件改变 state
13 let data = await this.getData('https://httpbin.org/uuid').then(d => d.uuid)
14 this.setState({ msg: data })
15 }
16 // 4
17 componentDidMount() {
18 document.title = this.state.msg
19 }
20 componentDidUpdate() {
21 document.title = this.state.msg
22 }
23 // 5
24 render() {
25 return (
26 <div onClick={this.handleClick}>{this.state.msg}</div>
27 )
28 }
29}
ClassTest
state
componentDidMount
和 componentDidUpdate
阶段改变 document.title
render
函数渲染这一堆东西写完人都睡着了?
使用 useEffect 不仅去掉了部分不必要的东西,而且合并了 componentDidMount
和 componentDidUpdate
方法,其中的代码只需要写一遍。?
第一次渲染和每次更新之后都会触发这个钩子,如果需要手动修改自定义触发规则
见文档:https://zh-hans.reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects
另外,官网还给了一个订阅清除订阅的例子:
20190313113615.png
使用 useEffect
直接 return
一个函数即可:
20190313113627.png
返回的函数是选填的,可以使用也可以不使用:
20190313113753.png
文档:https://zh-hans.reactjs.org/docs/hooks-effect.html#recap
比方说我们使用 useEffect 来解绑事件处理函数:
1useEffect(() => {
2 window.addEventListener('keydown', handleKeydown);
3 return () => {
4 window.removeEventListener('keydown', handleKeydown);
5 }
6})
useContext
的最大的改变是可以在使用 Consumer
的时候不必在包裹 Children
了,比方说我们先创建一个上下文,这个上下文里头有一个名为 username
的 state
,以及一个修改 username
的方法 handleChangeUsername
不使用 useState:
不使用 state hooks
的代码如下:
1import React, { createContext } from 'react'
2
3// 1. 使用 createContext 创建上下文
4export const UserContext = new createContext()
5
6// 2. 创建 Provider
7export class UserProvider extends React.Component {
8
9 handleChangeUsername = (val) => {
10 this.setState({ username: val })
11 }
12
13 state = {
14 username: '',
15 handleChangeUsername: this.handleChangeUsername
16 }
17
18 render() {
19 return (
20 <UserContext.Provider value={this.state}>
21 {this.props.children}
22 </UserContext.Provider>
23 )
24 }
25}
26
27// 3. 创建 Consumer
28export const UserConsumer = UserContext.Consumer
29
看看我们做了什么:
createContext
创建了上下文Provider
;里头定义了 handleChangeUsername
方法和 username
的 state
,并返回一个包裹 this.props.children
的 Provider
UserContext.Consumer
代码比较冗长,可以使用上文提到的 useState
对其进行精简:
✅使用 useState:
使用 state hooks
:
1import React, { createContext, useState } from 'react'
2
3// 1. 使用 createContext 创建上下文
4export const UserContext = new createContext()
5
6// 2. 创建 Provider
7export const UserProvider = props => {
8 let [username, handleChangeUsername] = useState('')
9 return (
10 <UserContext.Provider value={{username, handleChangeUsername}}>
11 {props.children}
12 </UserContext.Provider>
13 )
14}
15
16// 3. 创建 Consumer
17export const UserConsumer = UserContext.Consumer
18
使用 useState
创建上下文更加简练。
上下文定义完毕后,我们再来看使用 useContext
和不使用 useContext
的区别是啥?:
不使用 useContext:
1import React from "react";
2import { UserConsumer, UserProvider } from './UserContext'
3
4const Pannel = () => (
5 <UserConsumer>
6 {/* 不使用 useContext 需要调用 Consumer 包裹 children */}
7 {({ username, handleChangeUsername }) => (
8 <div>
9 <div>user: {username}</div>
10 <input onChange={e => handleChangeUsername(e.target.value)} />
11 </div>
12 )}
13 </UserConsumer>
14)
15
16const Form = () => <Pannel></Pannel>
17
18const App = () => (
19 <div>
20 <UserProvider>
21 <Form></Form>
22 </UserProvider>
23 </div>
24)
25
26export default App;
27
✅使用 useContext:
只需要引入 UserContext
,使用 useContext
方法即可:
1import React, { useContext } from "react"; // 1
2import { UserProvider, UserContext } from './UserContext' // 2
3
4const Pannel = () => {
5 const { username, handleChangeUsername } = useContext(UserContext) // 3
6 return (
7 <div>
8 <div>user: {username}</div>
9 <input onChange={e => handleChangeUsername(e.target.value)} />
10 </div>
11 )
12}
13
14const Form = () => <Pannel></Pannel>
15
16// 4
17const App = () => (
18 <div>
19 <UserProvider>
20 <Form></Form>
21 </UserProvider>
22 </div>
23)
24
25export default App;
26
看看做了啥:
useContext
UserProvider
以及上下文 UserContext
useContext
方法获取 state
UserProvider
嵌套主 children
这样通过 useContext
和 useState
就重构完毕了,看起来代码又少了不少?
以上,三个基础的 Hooks
入门就讲解完毕了,上手就是这样,函数式组件和 Hooks
配合使用真的非常爽⛄
参考: