热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

reacthooks初学者入门总结

reacthooks初学者入门总结近期接触reacthooks,在这里总结一下学习之路。总的来说,reacthooks的学习就是几个方法的学习
react hooks初学者入门总结

近期接触react hooks,在这里总结一下学习之路。总的来说,react hooks的学习就是几个方法的学习,废话不多说,起飞!

为什么要用hooks


  • 状态逻辑难以复用: 业务变得复杂之后,组件之间共享状态变得频繁,组件复用和状态逻辑管理就变得十分复杂。使用 redux 也会加大项目的复杂度和体积。
  • 组成复杂难以维护: 复杂的组件中有各种难以管理的状态和副作用,在同一个生命周期中你可能会因为不同情况写出各种不相关的逻辑,但实际上我们通常希望一个函数只做一件事情。
  • 类的 this 指向性问题: 我们用 class 来创建 react 组件时,为了保证 this 的指向正确,我们要经常写这样的代码:const that = this,或者是this.handleClick = this.handleClick.bind(this)>;一旦 this 使用错误,各种 bug 就随之而来。

为了解决这些麻烦,hooks 允许我们使用简单的特殊函数实现 class 的各种功能。

useState


设置和改变 state,代替原来的 state 和 setState

在 React 组件中,我们经常要使用 state 来进行数据的实时响应,根据 state 的变化重新渲染组件更新视图。

因为纯函数不能有状态,在 hooks 中,useState就是一个用于为函数组件引入状态(state)的状态钩子。

在类组件中我们更新状态是这样的

// 添加todo_addTodo(todo){// 1)插入todolet { todos } = this.state;todos.push(todo)// 2)更新状态机this.setState({todos})}

​ 现在在函数组件中我们更新状态是使用useState

function App() {const [count,setState] &#61; useState(0);return (<div className&#61;"App"><p>你点击了{count}</p><button onClick&#61;{()&#61;>{setState(count&#43;1)}}>点击</button></div>);
}export default App;
// count 声明的变量
// setCount 设用来更新变量的函数
// 0 初始值 初始值可以是数字&#xff0c;字符串等&#xff0c;也可以是对象或者数组。
// 多个状态声明不能出现在条件判断语句中

useState 的唯一参数是状态初始值&#xff08;initial state&#xff09;&#xff0c;它返回了一个数组&#xff0c;这个数组的第[0]项是当前当前的状态值&#xff0c;第[1]项是可以改变状态值的方法函数。

可以理解为类组件中的setState&#xff0c;相同的是都是异步的&#xff0c;不同的是类中的 setState 是合并&#xff0c;而函数组件中的 setState 是替换。这个初始 state 参数只有在第一次渲染时会被用到。


useEffect


引入具有副作用的操作&#xff0c;类比原来的生命周期

之前在类组件中有没有被生命周期函数折磨到&#xff1f;很多的生命周期函数&#xff0c;而且还很长&#xff0c;每一个什么功能什么时候可以使用&#xff0c;是不是很困扰呢&#xff1f;
在hooks里面就有一个很厉害的hook替代了三个常用的生命周期钩子&#xff0c;componentDidMount&#xff0c;componentDidUpdate和componentWillUnmount。我们写的有状态组件&#xff0c;通常会产生很多的副作用&#xff08;side effect&#xff09;&#xff0c;比如发起ajax请求获取数据&#xff0c;添加一些监听的注册和取消注册&#xff0c;手动修改dom等等。hooks是可以反复多次使用&#xff0c;相互独立的。所以我们给每一个副作用一个单独的useEffect钩子。这样一来&#xff0c;这些副作用不再一股脑堆在生命周期钩子里&#xff0c;代码变得更加清晰。

useEffect(()&#61;>{// some code
}, [])

第二个参数是[] 类似于生命周期函数的 componentDidMount。此时React的 effect 不依赖于 props 或 state 中的任何值&#xff0c;所以它永远都不需要重复执行&#xff0c;只执行一次。可以在这个hook里面发送ajax请求。

useEffect(()&#61;>{// some code
}, [count])

第二个参数[count]&#xff0c; 只有监听到count发生改变的时候才会更新

useEffect(()&#61;>{return () &#61;> { // some code}
})
// 返回一个函数实现解绑

useCallback


类似 useMemo&#xff0c;useMemo 优化传值&#xff0c;usecallback 优化传入的方法

第二个参数传入一个数组&#xff0c;数组中的每一项一旦值或者引用发生改变&#xff0c;useCallback 就会重新返回一个新的记忆函数提供给后面进行渲染&#xff0c;可以有效避免不必要的 vDOM 渲染。

function App() {const handleClick &#61; useCallback(() &#61;> {console.log(&#39;Click happened&#39;)}, []); // 空数组代表无论什么情况下该函数都不会发生改变return <SomeComponent onClick&#61;{handleClick}>Click Me</SomeComponent>;
}

useMemo


可根据状态变化控制方法执行&#xff0c;优化无用渲染&#xff0c;提高性能

useMemo 与 useCallback 类似&#xff0c;都是有着缓存的作用。useMemo 是缓存值的&#xff0c;useCallback 是缓存函数的。

const data &#61; useMemo(()&#61;>{return {name}},[name])

先根据[name]里面的name值判断一下&#xff0c;因为useMemo 作为一个有着暂存能力的&#xff0c;暂存了上一次的name结果。结果一对比上一次的name&#xff0c;我们发现name值居然没有改变&#xff01;那么这次data就不重新赋值成新的对象了&#xff01;memo是浅比较&#xff0c;意思是&#xff0c;对象只比较内存地址&#xff0c;只要你内存地址没变&#xff0c;管你对象里面的值千变万化都不会触发render。

useCallback 不会执行第一个参数函数&#xff0c;而是将它返回给你&#xff0c;而 useMemo 会执行第一个函数并且将函数执行结果返回给你。
useCallback 常用记忆事件函数&#xff0c;生成记忆后的事件函数并传递给子组件使用。useMemo 更适合经过函数计算得到一个确定的值。


useRef


返回一个可变的 ref 对象

useRef 返回一个可变的 ref 对象&#xff0c;其 .current 属性被初始化为传递的参数&#xff08;initialValue&#xff09;,是一个通用容器&#xff0c;其 current 属性是可变的&#xff0c;可以保存任何值&#xff08;可以是元素、对象、基本类型、甚至函数&#xff09;&#xff0c;类似于类组件用 React.createRef&#xff0c;。

import React, { useRef, useState, useEffect } from &#39;react&#39;;
function StudyuseRef(){// 使用 useRef 创建 inputEl const inputEl &#61; useRef(null);const [text, updateText] &#61; useState(&#39;&#39;);// 使用 useRef 创建 textRef const textRef &#61; useRef();useEffect(() &#61;> {// 将 text 值存入 textRef.current 中textRef.current &#61; text;console.log(&#39;textRef.current&#xff1a;&#39;, textRef.current);});const onButtonClick &#61; () &#61;> {inputEl.current.value &#61; "Hello, useRef";};return (<>{/* 保存 input 的 ref 到 inputEl */}<input ref&#61;{ inputEl } type&#61;"text" /><button onClick&#61;{ onButtonClick }>在 input 上展示文字</button><br /><input value&#61;{text} onChange&#61;{e &#61;> updateText(e.target.value)} /></>);
}export default StudyuseRef;

点击 在 input 上展示文字 按钮&#xff0c;就可以看到第一个 input 上出现 Hello, useRef&#xff1b;在第二个 input 中输入内容&#xff0c;可以看到控制台打印出对应的内容。

useContext


上下文爷孙组件及更深层组件传值

状态全局化并能实现统一管理&#xff0c;便于子父组件状态共享。

import React,{ useState ,createContext, useContext} from "react";
// 在它们的父组件上使用React的Context API&#xff0c;在组件外部建立一个Context。
const CountContext &#61; createContext();
//子组件的代码
function Counter(){// useContext()用来引入Context对象&#xff0c;从中获取count属性。let count &#61; useContext(CountContext)return(<h2>{count}</h2>)
}function StudyuseContext() {const [count,setState] &#61; useState(0); return (<div><p>你点击了{count}</p><button onClick&#61;{()&#61;>{setState(count&#43;1)}}>点击</button>//组件封装代码 CountContext.Provider提供了一个Context对象&#xff0c;这个对象是可以被子组件共享的。<CountContext.Provider value&#61;{count}><Counter></Counter></CountContext.Provider></div>);
}export default StudyuseContext;

useReducer


代替原来 redux 里的 reducer&#xff0c;方便管理状态逻辑

​ 提供类似 Redux 的功能&#xff0c;引入 useReducer 后&#xff0c;useReducer 接受一个 reducer 函数作为参数&#xff0c;reducer 接受两个参数一个是 state 另一个是 action 。然后返回一个状态 count 和 dispath&#xff0c;count 是返回状态中的值&#xff0c;而 dispatch 是一个可以发布事件来更新 state 的。通过dispatch action更新逻辑复杂状态&#xff0c;控制业务逻辑。

import React,{ useReducer} from "react";function StudyuseReducer() {const [count , dispatch]&#61;useReducer((state,action)&#61;>{switch(action){case &#39;add&#39;:return state&#43;1case &#39;sub&#39;:return state-1default:return state}},0)return (<div><p>现在的分数是{count}</p><button onClick&#61;{()&#61;>{dispatch(&#39;add&#39;)}}></button><button onClick&#61;{()&#61;>{dispatch(&#39;sub&#39;)}}></button></div>);
}export default StudyuseReducer;

对比 useState 可知&#xff0c;看起来我们的代码好像变得复杂了&#xff0c;但实际应用到复杂的项目环境中&#xff0c;将状态管理和代码逻辑放到一起管理&#xff0c;使我们的代码具有更好的可读性、可维护性和可预测性。

优化&#xff1a;延迟初始化
还可以懒惰地创建初始状态。为此&#xff0c;您可以将init函数作为第三个参数传递。初始状态将设置为 init(initialArg)。


useContext 与 useReducer 实现redux的状态管理和状态共享

实现状态全局化并能统一管理,统一个事件的派发。实现一个点击按钮改变字体颜色的小案例&#xff1a;

// 字体组件
import React ,{useContext}from "react";
import {ColorContext} from "./colors"function ShowArea(){// 获取colorconst {color} &#61; useContext(ColorContext)return (<div style&#61;{{color:color}}>字体颜色为{color}</div>)
}
export default ShowArea;

//按钮组件
import React ,{useContext,}from "react";import {ColorContext,UPDATE_COLOR} from &#39;./colors&#39;;function Buttons(){// 获取共享的dispatchconst {dispatch} &#61; useContext(ColorContext)return(<div>{/* 使用dispatch派发一个action */}<button onClick&#61;{()&#61;>{dispatch({type:UPDATE_COLOR,color:&#39;red&#39;})}}>红色</button><button onClick&#61;{()&#61;>{dispatch({type:UPDATE_COLOR,color:&#39;yellow&#39;})}}>黄色</button></div>)
}
export default Buttons;

//color组件 状态管理
import React ,{ createContext , useReducer} from "react";export const ColorContext &#61; createContext({})export const UPDATE_COLOR &#61; "UPDATE_COLOR"
// 定义reducer
const reducer &#61;(state,action)&#61;>{switch(action.type){case UPDATE_COLOR:return action.colordefault:return state}
}// 颜色共享
export const Color &#61; props &#61;>{// 使用reducerconst [color,dispatch]&#61; useReducer(reducer,&#39;blue&#39;)return({/* 将color和dispatch共享出去 */}<ColorContext.Provider value&#61;{{color,dispatch}}>{props.children}</ColorContext.Provider>)
}

//父组件
import React from "react";
import Buttons from "./Buttons";
import { Color } from "./colors";
import ShowArea from "./showArea";function Example(){return (<div><Color><ShowArea></ShowArea><Buttons></Buttons></Color></div>)
}
export default Example;

useSelector

作用&#xff1a;共享状态,从Redux的store中提取数据&#xff08;state&#xff09;

通过createStore将state存入store中&#xff0c;再将state暴露出来给子组件&#xff0c;通过store在子父组件中共享状态。

const num&#61;useSelector(state&#61;>state.num)

useDispatch

作用&#xff1a;共享状态&#xff0c;返回Redux的store中对dispatch的引用&#xff0c;可执行redux中的方法

const dispatch&#61;useDispatch()

useImperativeHandle


可以让你在使用 ref 时自定义暴露给父组件的实例值

useImperativeHandle 的第一个参数是定义 current 对象的 ref&#xff0c;第二个参数是一个函数&#xff0c;返回值是一个对象&#xff0c;即这个 ref 的 current 对象&#xff0c;这样可以像上面的案例一样&#xff0c;通过自定义父组件的 ref 来使用子组件 ref 的某些方法。

//子组件
useImperativeHandle(editorRef, () &#61;> ({initValue: value &#61;> {seteEitorState(BraftEditor.createEditorState(value));}}));// 父组件
export default () &#61;> {const editorRef &#61; useRef();const editorBool &#61; _.includes([COMP_TYPE.EDITOR,COMP_TYPE.TEXTEDITOR,COMP_TYPE.DIALOGBTN,COMP_TYPE.DESCMODULE,COMP_TYPE.DIALOGIMG], compType);// console.log(editorRef.current);if (editorRef.current && editorBool) {editorRef.current.initValue(editor.editorText);}}}, [moduleIndex, compIndex]);return (<div className&#61;"prop-config-text"><Form.Item name&#61;"editor" label&#61;"文本"><BraftEditorText editorRef&#61;{editorRef} /></Form.Item></div>);
};

自定义hooks

先说一下&#xff0c;其实官方的hook已经很多很全了&#xff0c;状态我们可以 useState &#xff0c;复杂多状态我们可以用useReducer &#xff0c;共享和传递状态可以使用 useContext &#xff0c;引用组件、引用状态可以 useRef &#xff0c;组件render完成之后的操作通过 useEffect 完成…还有其他几个hook&#xff0c;那么我们为什么还需要自定义hooks呢&#xff1f;

其实&#xff0c;自定义hook也是基于官方的hook进行组合&#xff0c;逻辑复用&#xff0c;业务代码解耦抽象后进一步提炼出来的具备一定功能的函数。它应当具有一定条件下的的通用性&#xff0c;可移植性。
目前的hook可能并不十分契合我们的需求&#xff0c;我们需要进行二次加工&#xff0c;成为我们的业务hook&#xff0c; 官方推荐自定义hook命名以use开头。


hooks使用以及编写规范


  1. 不要从常规 Javascript 函数调用 Hooks;
  2. 不要在循环&#xff0c;条件或嵌套函数中调用 Hooks;
  3. 必须在组件的顶层调用 Hooks;
  4. 可以从 React 功能组件调用 Hooks;
  5. 可以从自定义 Hooks 中调用 Hooks;
  6. 自定义 Hooks 必须使用 use 开头&#xff0c;这是一种约定;

推荐阅读
author-avatar
方家菱芝合
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有