1:为什么需要react-Hooks
Hook 时react 16.8的新增特性,它可以让我们在不编写class的情况下使用state以及其他的react特性(比如生命周期)
class组件相对于函数式组件的优势
(1)
√√√ class组件可以定义自己的state,用来保存组件内部的状态。
xxx 函数式组件不可以,因为函数每次调用都会产生新的临时变量。
(2)
√√√ class组件有自己的生命周期,我们可以在对应的生命周期中完成自己的逻辑。比如在componentDIdMount种发送网络请,并且该生命函数只会执行一次。
xxx 函数式组件在学习hooks之前,如果在函数中发送网络请求,意味着每次重新渲染都会发送一次网络请求。
(3)
√√√ class组件在状态改变时只会重新执行render函数,以及我们希望重新调用新的生命周期函数componentDidUpdate等
xxx 函数式组件在重新渲染的时候,整个函数都会被执行,似乎没什么地方可以只让他们被调用一次。
所以:在Hook出现之前,对于上面这些情况我们通常会编写class组件。
class组件存在的问题
- 我们最初在编写一个class组件的时候,往往逻辑比较简,但是随着业务需求的增加,业务往往会比较多,导致我们的calss组件变得越来越复杂。
- 对于这样的class实际上非常难以拆分:因为他们的逻辑往往混在一起,强行拆分反而会造成过度设计,增加代码的复杂度。
- 对于不是很熟悉ES6的开发人员而言,ES6的class式学习React的一个障碍。
- 组件状态的复用比较难。包括共享状态等。
2:Hook的出现
Hook的出现可以解决上面的问题。
== 简单总结一下Hooks:==
它可以让我们在不编写class的情况下使用state以及其他的React特性
但是我们可以由此延申初许多的写法,来让我们前面提到的问题得到解决.
Hook的横空出现
Hook的使用场景:
Hook的出现基本可以替代我们之前使用class组建的地方(除了一些非常不常用的场景)
Hook只能在函数组件中使用,不能再类组件或者函数式组件之外的地方使用它。
3.Hook的初体验
接下来,我们将通过一个计数器案例来对比一下,class组件和Function组件的区别。以及了解一下hook是怎么使用的。
使用class组件来实现一个计数器
import React, {
PureComponent} from 'react'
export default class CounterClass extends PureComponent {
constructor(props){
super(props)
this.state = {
counter:0
}
}
render() {
return (
<div>
<h2>当前计数:{
this.state.counter}</h2>
<button onClick={
e => this.increment()}>+1</button>
<button onClick={
e => this.decrement()}>-1</button>
</div>
)
}
increment(){
this.setState({
counter:this.state.counter + 1
})
}
decrement(){
this.setState({
counter:this.state.counter - 1
})
}
}
使用HOOK(结合function)来实现一个计数器
import React, {
useState} from 'react'
export default function CounterHook2() {
const [count, setCount] = useState(0);
return (
<div>
<h2>当前计数: {
count}</h2>
<button onClick={
e => setCount(count + 1)}>+1</button>
<button onClick={
e => setCount(count - 1)}>-1</button>
</div>
)
}
hook中处理状态的部分:
/**
* Hook : useState 使用状态
* > 本身是一个函数,函数来自我们的react这个包
* > 参数和返回值
* 1.参数:作用是给创建出来的状态一个默认值
* 2.返回值:
* 元素1:当前state的值
* 元素2:设置新的值的时候返回的一个函数
*/
Hook就是javascript函数,这个函数可以帮助你钩入(hook into)react state以及生命周期等特性。
但是他在使用的时候会有两个额外的规则:
1 只能再函数最外层带哦用hook,不要再循环没条件判断,或者子函数中调用。
2 只能再React的函数组件中调用hook。不要再其他javascript函数中调用。
使用HOOK 当一个组件内部存在多个变量的时候的处理方式
处理复杂的数据:
import React, {
useState} from 'react'
export default function ComplexHookState() {
const [friends, setFriends] = useState(['js','java'])
return (
<div>
<h2>好友列表</h2>
<ul>
{
friends.map( (item,index) => {
return (
<li key={
index}>{
item}</li>
)
})
}
</ul>
<button onClick={
e => setFriends([...friends,"康家豪"])}>添加朋友</button>
</div>
)
}
处理复杂的数据:
import React, {
useState } from 'react'
export default function ComplexHookState() {
const [friends, setFriends] = useState(['js', 'java'])
const [students, setStudents] = useState([
{
id: 110, name: 'why', age: 19 },
{
id: 112, name: 'kobe', age: 30 },
{
id: 120, name: 'lilei', age: 78 }
])
function studentsAgeIncrement(index) {
const newStudents = [...students];
newStudents[index].age += 1;
setStudents(newStudents)
}
return (
<div>
<h2>好友列表</h2>
<ul>
{
friends.map((item, index) => {
return (
<li key={
index}>{
item}</li>
)
})
}
</ul>
<button onClick={
e => setFriends([...friends, "康家豪"])}>添加朋友</button>
<hr />
<h2>学生列表</h2>
<ul>
{
students.map((item, index) => {
return (
<li key={
item.id}>
id:{
item.id} name:{
item.name} age:{
item.age}
<button onClick={
e => studentsAgeIncrement(index)}>age+1</button>
</li>
)
})
}
</ul>
</div>
)
}
4.认识 Effect Hook
目前我们已经通过hook在函数式组件中定义state,那么类似于生命周期这些怎么实现呢/
Effect Hook 可以帮助你来完成一些类似于class中声明周期的功能.
现在我们有一个需求:页面的title显示当前的数字
使用class组件来实现:
import React, {
PureComponent } from 'react'
export default class ClassCounterTitleChange extends PureComponent {
constructor(props){
super(props)
this.state = {
counter:0
}
}
//类组件使用生命周期来实现 title 根据counter来发生改变
componentDidMount() {
document.title = this.state.counter;
}
componentDidUpdate() {
document.title = this.state.counter;
}
render() {
return (
<div>
<h2>当前计数:{
this.state.counter}</h2>
<button onClick={
e => this.increment()}>add one</button>
</div>
)
}
increment(){
this.setState({
counter:this.state.counter + 1
})
}
}
使用Hook(结合useEffect)实现
import React, {
useState,useEffect} from 'react'
export default function HookCounterTitleChange() {
const [state, setstate] = useState(0)
/**
* useEffect的使用
* 可以用来模拟生命周期
*/
useEffect(() => {
document.title = state;
})
return (
<div>
<h2>当前计数:{
state}</h2>
<button onClick={
e => setstate(state + 1)}>+1</button>
</div>
)
}
useEffect模拟订阅和取消订阅
import React, {
useEffect, useState } from 'react'
export default function EffectHookCancleDemo() {
const [count, setCount] = useState(0)
useEffect(() => {
console.log("发生了事件订阅")
return () => {
console.log("取消了订阅事件")
}
},[])
return (
<div>
<h2>03_eFfect模拟订阅和取消订阅</h2>
<h2>当前计数:{
count}</h2>
<button onClick={
e => setCount(count + 1)}>+1</button>`
</div>
)
}
useEffect详解
useEffect ( () => {
console.log('修改DOM‘)
} )
useEffect 参数1: 传入一个箭头函数,页面刷新调用这个函数
useEffect 参数2: 传入一个数组,数组里传入依赖,当依赖中某个变量发生改变的时候就会再次调用useEffect函数执行。
useContext详解
useContext可以实现数据的共享:
举个例子:
先定义两个组件:
export const UserContext = createContext()
export const ThemeContext = createContext()
然后下方嵌套使用:
<UserContext.Provider value={
{
name: '康家豪', age: 18 }}>
<ThemeContext.Provider>
<UseContextDemo value={
{
weather: "多云", home: '宝鸡' }} />
</ThemeContext.Provider>
</UserContext.Provider>
子组件中获取数据:
import React from 'react'
import {
useContext,useState } from 'react'
import {
UserContext, ThemeContext } from '../App'
export default function UseContextDemo(props) {
const user = useContext(UserContext);
const theme = useContext(ThemeContext)
console.log(user,theme,props)
return (
<div>
<h2>useContextDemo</h2>
</div>
)
}
5.补充的一些Hooks
useReducer:useState数据逻辑复杂时的替代方案,可以在UseReducer中拆分逻辑
UseCallBack实际的目的是为了进行性能的优化
useCallBack 会返回一个函数的memoized(记忆的)值’
在依赖不变的情况下,多次定义的时候,返回的值是相同的。
举个例子:
import React, {
useState,useCallback} from 'react'
export default function CallBackHookDemo01() {
const [count, setCount] = useState(0)
const increment = () => {
console.log('执行increment1函数');
setCount(count + 1)
}
//使用useCallBack进行性能优化
//没有任何依赖的时候,count永远都是从0开始计算的
const increment2 = useCallback(() => {
console.log('执行increment2函数');
setCount(count + 8)
},[])
return (
<div>
当前计数:{
count}
<button onClick={
increment}>按钮1</button>
<button onClick={
increment2}>按钮2</button>
</div>
)
}
UseMemo实际的目的是为了进行性能的优化
useMemo 会返回一个函数的memoized(记忆的)值’
在依赖不变的情况下,多次定义的时候,返回的值是相同的。
import React from 'react'
import {
useState } from 'react'
export default function UseMemo1() {
const [count, setCount] = useState(10)
const [show, setShow] = useState(true)
let total = 0;
for(var i = 0; i < count; i++){
total += i;
}
return (
<div>
<h2>计算数字的和:{
total}</h2>
<button onClick={
e => setCount(count + 1)}>+1</button>
<button onClick={
e => setShow(!show)}>show切换</button>
</div>
)
}
当我们执行show切换的时候,这个时候上面的累加函数就会重新执行一次,影响网站的性能。
import React, {
useMemo } from 'react'
import {
useState } from 'react'
function calcCount(count){
console.log('calcCount重新计算')
let total = 0;
for(var i = 0; i < count; i++){
total += i;
}
return total;
}
export default function UseMemo1() {
const [count, setCount] = useState(10)
const [show, setShow] = useState(true)
// const total = calcCount(count);
//使用useMemo来优化函数
const total = useMemo(() => {
return calcCount(count)
},[count])
//传入依赖count 表明当count发生改变的时候,这里的代码才会重新执行一次
return (
<div>
<h2>计算数字的和:{
total}</h2>
<button onClick={
e => setCount(count + 1)}>+1</button>
<button onClick={
e => setShow(!show)}>show切换</button>
</div>
)
}
UseRef
用法1:引入DOM(或者组件,但是需要是class组件)元素
用法2:保存一个数据,让这个对象在整个生命周期中可以保存不变。
用法一代码示例:
import React ,{
useRef}from 'react'
class TestCpn extends React.Component {
render(){
return <h2>天气很好</h2>
}
}
export default function RefHookDemo01() {
const titleRef = useRef()
const cpn1 = useRef()
function changeDOM(){
titleRef.current.innerHTML = "HELLO WORLD"
console.log(cpn1.current)
}
return (
<div>
<h2 ref={
titleRef}>RefHookDemo01</h2>
<button onClick={
e => changeDOM() }>修改DOM</button>
<TestCpn ref={
cpn1}/>
</div>
)
}
这样可以使用useRef来调用DOM元素和class 组件(函数式组件不可以使用useRef)
用法二代码示例:
import React,{
useRef, useState, useEffect} from 'react'
export default function RefHookDemo02() {
const [count, setCount] = useState(0)
// console.log(count)
//这里增加count的时候,count的确增加了
const numRef = useRef(count);
//使用useRef会保留最原本的值
useEffect(() => {
numRef.current = count;
}, [count])
return (
<div>
<h2>RefHookDemo02</h2>
<h2>上一次count的值:{
numRef.current}</h2>
<h2>当前的count的值:{
count}</h2>
<button onClick={
e => setCount(count + 1)}>+1</button>
</div>
)
}
可以通过useRef实现一个不会更改的数据。
可以通过useRef 配合 useEffect来实现保存上一次数据状态。
UseImperativeHandle
觉得有一丝毛用就没整理了。
UseLayoutEffect
4.自定义Hook
认识自定义hook
import React,{
useEffect} from 'react'
function Home(){
useLoginAndCancle('home')
return <h2>Home</h2>
}
function About(){
useLoginAndCancle('about')
return <h2>About</h2>
}
export default function CustomLifeHookDemo01() {
useLoginAndCancle('customLifeHookDemo')
return (
<div>
CustomLifeHookDemo01
<Home/>
<About/>
</div>
)
}
function useLoginAndCancle(name){
useEffect(() => {
console.log(`${
name}被创建出来了`)
return () => {
console.log(`${
name}组件被销毁了`)
}
}, [])
}
自定义获取页面scroolY的hook
import React from 'react'
import {
useEffect } from 'react'
export default function HookScrool() {
useEffect(() => {
const handleScroll = () => {
console.log(window.scrollY)
}
document.addEventListener('scroll',handleScroll)
return () => {
document.removeEventListener('scroll',handleScroll)
}
})
return (
<div style={
{
padding:"1000px 0"}}>
<h2>
HookScrool
</h2>
</div>
)
}
当然最后我们在项目开发中,可以将hook封装为一个文件夹,将我们对应写好的自定义hook封装好并且导出,这样我们就可以在爱多个组件内部使用我们封装好的自定义hook。
————————————————————————————————————————--------
react hooks 补充
react hooks 配合 redux进行使用
import React, {
memo, useEffect } from 'react';
import {
connect, useDispatch, useSelector } from 'react-redux';
import {
getTopBannerAction } from './store/actionCreators'
function KJHRecommend(props) {
// 组件和redux关联: 获取数据和进行操作
const dispatch = useDispatch();
// 发送网络请求
useEffect(() => {
dispatch(getTopBannerAction());
}, [dispatch])
//拿到数据
const {
topBanners} = useSelector(state => ({
topBanners: state.recommend.topBanners
}))
return (
<div>
<h2>KJHRecommend : {
topBanners.length}</h2>
</div>
)
}
export default memo(KJHRecommend);
useDispatch的使用
const dispatch = useDispatch()
主要作用:
返回Redux store中对dispatch函数的引用。你可以根据需要使用它。
useSelect的使用
const result : any = useSelector(selector : Function, equalityFn? : Function)
主要作用:
从redux的store对象中提取数据(state)。
总有一天我会忘了你,但是我永远欣赏你,我的姑娘。