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/docs/hooks-state.html
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/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects
另外, 官网还给了一个订阅清除订阅的例子:
使用 useEffect 直接 return 一个函数即可:
返回的函数是选填的, 可以使用也可以不使用:
文档: https://zh-hans.reactjs.org/docs/hooks-effect.html#recap
比方说我们使用 useEffect 来解绑事件处理函数:
- useEffect(() => {
- Windows.addEventListener('keydown', handleKeydown);
- return () => {
- Windows.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 配合使用真的非常爽
参考:
- https://codeburst.io/quick-intro-to-react-hooks-6dd8ecb898ed
- https://reactjs.org/docs/hooks-reference.html
来源: https://juejin.im/post/5c8918ca6fb9a049f572023e