React-Hooks
一. useState
useState基本使用方式
- const [count,setCount] = useState(0)
- count:我们定义的状态
- setCount:操作状态的方法 Function
- useState(0):函数,参数是我们给状态的初始值
//定义一个函数式组件
import React, { useState } from "react"
export default function FuncCom() {
/*
引入useState
数组解构
1. 第一个值是我们定义的参数
2. 第二个值是操作参数的方法 使用方式: setCount(count+1)
3. useState(0) 定义count初始值为0
*/
const [count, setCount] = useState(0)
return (<div>
<h2>function Component {count}
<button onClick={() => { setCount(count + 1) }}>count+1</button>
</h2>
</div>)
}
二. useEffect
作用:代替生命周期函数
componentDidMountcomponentDidUpdatecomponentWillUnmount
-
useEffect在dom被创建、更新、销毁的时候调用
-
结构
useEffect(*Function*,*Array*) -
useEffect可以代替
componentDidMountcomponentWillUnmountcomponentDidUpdate -
useEffect中第一个参数为函数,第二个参数是一个数组
useEffect(()=>{
//函数体
return ()=>{
//返回函数
}
},[])
-
函数中的函数体是在组件创建和更新时调用,返回值函数在组件被销毁时调用
-
数组中的元素对函数的调用做约束,数组中的元素发生改变,函数被调用,数组为空则不调用
import React, { useState, useEffect } from "react"
import { BrowserRouter, Route, Link, Switch } from "react-router-dom"
function Page1() {
useEffect(() => {
console.log('page1组件被创建或者更新')
return () => {
//返回的函数在组件被销毁时执行
console.log('page1组件被销毁')
}
}, [])
return (
<h3>page1</h3>
)
}
function Page2() {
useEffect(() => {
console.log('page2组件被创建或者更新')
}, [])
return (
<h3>page2</h3>
)
}
export default function FuncDemo() {
//useState用于创建组件状态
const [count, countAdd] = useState(0)
const handleCount = () => countAdd(count + 1)
useEffect(() => {
//函数体在组件更新和创建时调用
console.log('dom has been update')
return () => {
//返回的函数在组件被销毁时执行
console.log('组件被销毁')
}
}, [count])
return (
<BrowserRouter>
<div>
{count}
<button onClick={handleCount}>count+</button>
--------------------------------------
<ul>
<li><Link to="/page1">page1</Link></li>
<li><Link to="/page2">page2</Link></li>
</ul>
<Switch>
<Route path="/page1" component={Page1}></Route>
<Route path="/page2" component={Page2}></Route>
</Switch>
</div>
</BrowserRouter>
)
}
三. useContext
-
引入
createContextuseContext -
创建上下文组件 const CountContext = createContext()
-
子组件内获取状态 let count = useContext(CountContext)
-
父组件提供数据
<CountContext.Provider value={count}> <Counter/> //子组件名 </CountContext.Provider>
使用实例
import React, { useState, createContext, useContext } from "react"
const CountContext = createContext()
// 子组件
function ShowCount() {
//使用useContext接收CountContext内容
let count = useContext(CountContext)
return (
<h2>{count}</h2>
)
}
//父组件
export default function UseContextInsteadOfProps() {
const [count, setCount] = useState(0)
return (
<div>
<h1>UseContextInsteadOfProps</h1>
<h1>{count}</h1>
<button onClick={() => { setCount(count + 1) }}>setCount</button>
{/* 使用前面创建的CountContext组件传参 */}
<CountContext.Provider value={count}>
{/* 在CountContext内显示子组件 */}
<ShowCount />
</CountContext.Provider>
</div>
)
}
四. useReducer
-
useReducer基本结构
//定义 //count:我们监听的状态 //dispatch:分发事件的函数 //state:接收到count的形参 //action:调用dispatch时传递的参数 const [count,dispatch] = useReducer((state,action)=>{ switch(action){ case 'per1': return 'opration1', case 'per2': return 'opration2' ... default: return initstate } },initState) //使用 ()=>dispatch('per1') -
实例
import { ReactElement, useReducer } from "react";
//定义初始化变量
let initialCount: number = 0;
//定义reducer函数 和redux一样,两个参数,state和action
const count_reducer = (
state: number,
action: { type: string, data: number }
): number => {
//数据处理
const { type, data } = action;
switch (type) {
case "add":
return state + data;
case "sub":
return state - data;
default:
return initialCount;
}
}
const LearnReducer: React.FC = (): ReactElement => {
//useReducer进行状态管理
const [count,dispatch] = useReducer(count_reducer,initialCount)
return (
<>
<h1>{count}</h1>
{/*使用dispatch分发状态管理事件*/}
<button onClick={() => dispatch({ type: "add", data: 5 })}>add5</button>
<button onClick={() => dispatch({ type: "sub", data: 5 })}>sub5</button>
<button onClick={() => dispatch({ type: "",data:3})}>reset</button>
</>
)
}
export default LearnReducer
五. useMemo
由于默认情况下,父组件状态改变后,子组件也会重新渲染,这样会导致严重的效率问题,让原本没必要重新渲染的组件被重新渲染
实例:
import React, { useState, useMemo } from "react"
export default function LearnUseMemo() {
//定义两个状态
const [m, setm] = useState(0)
const [n, setn] = useState(10)
return (
<div>
{/* 将状态传递给子组件 */}
< Child1 m={m} />
< Child2 n={n} />
{/* 点击按钮改变状态 */}
<button onClick={() => setm(m + 1)}>m++</button>
<button onClick={() => setn(n + 1)}>n++</button>
</div>
)
}
const Child1 = props => <h2>{props.m}</h2>
//只给Child2组件添加调用时打印内容
const Child2 = props => {
console.log('Child2被调用')
return <h2>{props.n}</h2>
}
- 一般看来,上面的组件是没有任何问题的
- 但是上面的组件有严重的效率问题
describe:
- 创建一个父组件LearnUseMemo
- 创建两个状态m,n
- 创建两个子组件Child1 Child2 分别将m,n传递给子组件
problem:
点击n会打印内容,没有问题,因为打印内容在子组件child2内
但是点击m也会打印内容,这是没有必要的,因为m状态的更新和child2组件的渲染没有任何关系,这是因为子组件更新默认重新渲染的是父组件,useMemo就是用来解决这种情况带来的问题
使用useMemo
-
useMemo(Callback,Array)
-
更改状态时将方法设置为useMemo的内部参数
-
Array的作用和useEffect一样
-
子组件最外层由
React.memo()包裹const Child1 = React.memo(props => <h2>{props.m}</h2>)
六 useCallback
useCallback是useMemo的语法糖,简化了useMemo内部回调混乱的问题
以上代码修改后
import React, { useState, useMemo, useCallback } from "react"
export default function LearnUseMemo() {
//定义两个状态
const [m, setm] = useState(0)
const [n, setn] = useState(10)
const addm = useMemo(() => {
return () => {
setm(m + 1)
}
}, [m])
//useCallback是useMemo的语法糖,简化了useMemo写法
const addn = useCallback(() => {
setn(n + 1)
}, [n])
return (
<div>
{/* 将状态传递给子组件 */}
< Child1 m={m} />
< Child2 n={n} />
{/* 点击按钮改变状态 () => setm(m + 1)*/}
<button onClick={addm}>m++</button>
<button onClick={addn}>n++</button>
</div>
)
}
const Child1 = React.memo(props => <h2>{props.m}</h2>)
//只给Child2组件添加调用时打印内容
const Child2 = React.memo(props => {
console.log('Child2被调用')
return <h2>{props.n}</h2>
})
七. useRef
调用useRef方法会创建一个对象,对象中只有一个属性current
将一个属性保存在内,它的地址一直不会变。
保存变量(少用)
调用方式:const c = useRef(0)
打印 c: {current:0}
import React, { useRef, useEffect, useState } from 'react'
const LearnUseRef_02 = () => {
//调用useRef储存状态
let r = useRef(0)
const [flag, setFlag] = useState()
const changeR = () => {
//直接在这里更改不会被渲染到dom上
r.current++
setFlag(r.current)
console.log('r.current:', r.current)
}
useEffect(() => {
console.log('flag:', flag);
})
return (
<div>
<h1>r.current:{r.current}</h1>
<button onClick={changeR}>changeR</button>
</div>
)
}
export default LearnUseRef_02
储存dom元素
调用方式:
1. const inputElement = useRef(null)
2. <input ref={inputElement}/>
3. alert(inputElement.current.value)
实例
//useRef挂载dom元素
import React, { useRef } from "react"
const LearnUseRef = () => {
//通过useRef初始化一个变量挂载dom节点
const inputEl = useRef(null)
const showInputData = () => {
//通过inputEl.current来获取dom节点
alert(inputEl.current.value)
console.log(inputEl) //{current: input}
}
return (
<div>
<input type="text" ref={inputEl} />
<button onClick={showInputData}>显示内容</button>
</div>
)
}
export default LearnUseRef
自定义hooks函数
//自定义hooks函数
import React, { useState, useEffect, useCallback } from "react";
function useWinSize() {
const [size, setSize] = useState({
width: document.body.clientWidth,
height: document.body.clientHeight
})
const onResize = useCallback(() => {
setSize({
width: document.body.clientWidth,
height: document.body.clientHeight
})
})
useEffect(() => {
window.addEventListener('resize', onResize)
return () => {
window.removeEventListener('resize', onResize)
}
})
return size
}
const DefinedHooks = () => {
const size = useWinSize()
console.log(size);
return (
<div>
<h1>宽:{size.width} 高:{size.height}</h1>
</div>
)
}
export default DefinedHooks