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