文章来源:https://www.bilibili.com/video/BV153411n7B5/?p=5&spm_id_from=333.880.my_history.page.click
1.为什么要用hooks ,单纯的使用class组件不行吗?
react是用于构建用户界面的JavaScript库,使用class组件有点大材小用,组件中很少用到class的继承,组件之间很少进行访问,但是使用函数式组件,他能更好的胜任根据状态来渲染UI的工作,hooks内部拥有了维护状态的能力,且hooks带来了逻辑复用的能力.
2.react模型的本质
react模型本质就是model->views的一个过程,给他数据,他将为你展示ui,即state+props=组件
3.hooks是什么
hooks是函数,是一个提供了状态和生命周期等特性的函数
4.在什么时候使用setState使用回调函数作为参数
1.初始状态需要经过一些计算,localStorage中拿值
2.状态需要迭代累加,setXxx((上一次的值)=>{return 新的值}),即多次调用setXxxx,页面显示的是多次调用setXxxx后的新值,而不是最后一个setXxx覆盖得到的新值.
function addClick(){
setCount(count + 1)
setCount(count + 1)
setCount(count + 1) //最后一次的值覆盖上面的值
}
function addClick(){
setCount((num)=>{
return num + 1
})
setCount((num)=>{
return num + 1
})
setCount((num)=>{ //最后一次的调用,是基于上次是调用结束后的值
return num + 1
})
}
//这两种调用方式在页面展示的内容是不一样的
5.useState更新视图的过程
1.setState的每次调用,都会使函数式组件里面的代码重新走一遍
2.useState只会在组件初次渲染的时候使用初始值,之后都会使用更新后的值
3.一个函数式组件中的state是共享的,多次渲染会相互影响
6.副作用,useEffect
函数式组件除了渲染UI之外的操作都算是副作用操作
1.主要作用:函数式组件里面的return 的jsx ; 定义的state等
2.副作用:请求,修改dom,定时器的定义和清除,localStarage等
推荐将副作用的代码写在useEffect里面
7.useEffect的执行时机
1.页面挂载的时候执行一次(componentDidMount)
2.页面更新的时候触发,可能触发多次(componentDidUpdate)
3.在useEffect中发送ajax请求的小细节
//1.错误写法
useEffect(async ()=>{
const res = await fetchGetApi(data)
console.log(res)
},[])
//2.正确写法
useEffect(()=>{
const getData = async ()=>{
const res = await fetchGetApi(data)
console.log(res)
}
getData()
},[])
//useEffect要求他的回调函数是同步的,加上了async会导致该回调函数成为异步函数
8.useEffect的第二个参数
1.没有参数时:
useEffect(()=>{
console.log("useEffect")
//1.页面初次挂载会执行,
//2.页面更新可能会多次执行
})
2.参数为[]
useEffect(()=>{
console.log("useEffect")
//1.页面初次挂载时执行,类似vue的created和mounted,可以用来发送请求
},[])
3.参数为数组,里面有值
const [n,setN] = useState(1)
const [info] = useState({name:"ls",age:12})
useEffect(()=>{
console.log("useEffect")
//1.页面初次挂载时执行,
//2.所依赖的项发生变化也会执行
},[n,info])
9.useEffect副作用函数的返回值,来清理副作用
1.useEffect副作用函数的返回值可省略,也可以返回一个清理函数,清理函数会在组件卸载时触发,也会在下一次副作用函数执行之前触发
useEffect(()=>{
console.log("副作用函数")
return '清理函数'
},[])
useEffect(()=>{
console.log("副作用函数")
return ()=>{
console.log("清理函数")
}
})
2.应用场景,例如移除window监听的事件
useEffect(()=>{
const fn = (e)=>{
console.log(e.pageX,e.pageY)
}
window.addEventListener('mousemove',fn)
return ()=>{
window.removeEventListener('mousemove',fn)
}
})
3.应用于类组件的componentWillUnmounted生命周期,即组件卸载时需要做的逻辑操作
useEffect(()=>{
return ()=>{
console.log("组件卸载时要做的操作")
}
},[])
10.自定义hooks,逻辑复用
自定义hooks提取公共逻辑,并且封装成一个函数,该函数以"use"开头,根据不用的参数可以有不同的返回值.自定义hooks只能用在函数式组件中或者其他自定义hooks中
import { useEffect, useRef, useState } from "react"
function CountDown() {
const [count, setCount] = useState(3)
const timeId = useRef(null)
useEffect(() => { //页面加载时执行的动作
setCount(3)
// 1.开始倒计时
timeId.current = setInterval(() => {
setCount(count=>count-1)
}, 1000);
}, [])
//倒计时结束需要清空定时器
useEffect(() => {
if (count === 0) {
console.log('倒计时结束执行的动作');
clearInterval(timeId.current)
}
}, [count])
//组件卸载时需要清空定时器
useEffect(() => {
return () => {
clearInterval(timeId.current)
}
},[])
return <div>1</div>
}
export default CountDown
提取成自定义hook后的代码
/**
*
* @param {*} initCount 初始倒计时的值,有用户传递,默认10s
* @param {*} callback 倒计时结束后需要执行的动作
* @returns 倒计时的数字和什么时候开始倒计时的函数
*/
export function useCountDown(initCount = 10, callback = () => { }) {
const [count,setCount] = useState(initCount)
const timeId = useRef(null)
const start = () => {
// 1.开始倒计时
setCount(initCount)
timeId.current = setInterval(() => {
setCount(count=>count-1)
}, 1000);
}
useEffect(() => {
if (count===0) {
clearInterval(timeId.current)
}
}, [count])
useEffect(() => {
return () => {
clearInterval(timeId.current)
}
},[])
return {
//需要将倒计时的数字返回回去
count,
//需要将什么时候开始执行倒计时返回
start
}
}
11.useRef
1.操作dom,获取组件
const inputRef = useRef(null)
const cusComp = useRef(null)
<input ref={inputRef}/>
<CusComp ref={cusComp}/>
inputRef.current //就是获取到的dom或者组件
2.在多次渲染之间共享数据
//错误示例,无法清除定时器
function CusComp(){
let timeId = null
const [count,setCount] = useState(0)
useEffect(()=>{
timeId = setInterval(()=>{
setCount(count=>count+1)
},1000)
},[])
const cancle = ()=>{
clearInterval(timeId)
}
return (<div>
{count}
<button onClick={cancle}></button>
<div/>)
}
//使用useRef实现多次渲染的数据共享
import {useState,useEffect,useRef } from 'react'
function CusComp(){
const [count, setCount] = useState(0)
const timeId = useRef(null)
useEffect(()=>{
timeId.current = setInterval(()=>{
setCount(count=>count+1)
}, 1000)
},[])
const cancle = () => {
clearInterval(timeId.current)
}
return <div>
{count}
<button onClick={cancle}>清除</button>
</div>
}
export default CusComp
12.useContext - 全局状态
使用步骤:
//1.第一步 使用Context.provider包裹住根组件
import { createContext } from "react"
export const Context = createContext()
function App(){
return <Context.Provider value={需要传递的值}>
<div>根组件</div>
</Context.Provider>
}
//2.在其他组件中接收
import { useContext } from "react"
import { Context } from "根组件"
function Son(){
const res = useContext(Context) //res 就是接收到的数据
return <div>son</div>
}
13.redux:javaScript应用的状态容器,提供可预测的状态管理
1.redex的核心概念:store ,action , reducer
action : 一个js对象,包含两个属性:
type:标识属性,值是字符串.多个type用action分开
payload:数据属性,可选.表示本次动作携带的数据
action只是描述了有事情发生这一事实,并没有描述应用如何更新state
reducer: 一个函数,可以修改状态
const reduer = (preState, action) = {}
store:仓库,核心,整合action和reducer
1.一个应用只有一个store
2.获取状态: store.getState()
3.创建store时,接收reducer作为参数:
const store = createStore(reducer)
4.发生状态更新时:需要分发action : store.dispatch(action)
2.订阅及取消状态变化
1.订阅状态变化:
const unSubscribe = store.subscribe(()=>{}) // 状态发生变化需要执行的逻辑
2.取消订阅 : unSubscribe()
3.先订阅,后执行action 会触发订阅的回掉函数
4.示例代码
import { createStore } from "redex"
const initState = {
count : 0
}
const action = { type:'add', payload:1}
const reducer = (prestate = initState,action) = {
switch (action.type) {
case 'add':
prestate.count += 1
return prestate
default:
return prestate
}
}
const store = createStore(reducer)
const unSubscribe = store.subscribe(()=>{})
store.dispatch(action)
14.react-redux 工具优化代码
1.引入Provider 将根组件App 包裹
import { Provider } from "react-redux"
import store from './store';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
,
document.querySelector('#root')
)
2.初始化store.js文件
import { legacy_createStore as createStore } from "redux"
import reducer from "./reducers"
const store = createStore(reducer)
export default store
3.创建reducer.js文件,拆分模块
import { combineReducers } from "redux"
import userInfo from "./userInfo"
import role from "./role"
const rootReducer = combineReducers({
userInfo,
role
})
export default rootReducer
4.根据业务,分不同的reducer模块
const userInfo = {
userName: '张三',
age:12
}
const reducer = (preState = userInfo, action) => {
switch (action.type) {
case 'userInfo/changeName':
return {...preState,userName:action.payload}
default:
return preState
}
}
export default reducer
const role = 0
const reducer = (preState = role, action) => {
switch (action.type) {
case 'role/changeRole':
return action.payload
default:
return preState
}
}
export default reducer
15.纯函数:相同的输入总是会得到想用的输出
16.react router
1.安装路由
npm i react-router-dom
2.引入路由
import { HashRouter, BrowserRouter, Link, NavLink, Route, Switch } from "react-router-dom"
3.编写路由规则
//hash模式的路由
<HashRouter>
<Link to='/home'>home</Link>
<Route path='/home' component={Home}></Route>
</HashRouter>
//history模式
//hash模式的路由
<BrowserRouter>
<NavLink to='/home' exact>home</NavLink>
<Route path='/home' component={Home}></Route>
</BrowserRouter>
link 和navLink的跳转基本一样,NavLink有一个activeClassName来指定高亮样式,默认active
NavLink还能exact精准匹配,默认模糊匹配
4.Switch匹配到就不会继续往下匹配了,404页面放下最下面
<BrowserRouter>
<NavLink to='/home' exact>home</NavLink>
<Switch>
<Route path='/home' component={Home}></Route>
<Route path='/home/detail' component={Home}></Route>
<Route path='/404' component={Page404}></Route>
</Switch>
</BrowserRouter>
exact>home
link 和navLink的跳转基本一样,NavLink有一个activeClassName来指定高亮样式,默认active
NavLink还能exact精准匹配,默认模糊匹配
4.Switch匹配到就不会继续往下匹配了,404页面放下最下面
home
<Switch>
<Route path='/home' component={Home}></Route>
<Route path='/home/detail' component={Home}></Route>
<Route path='/404' component={Page404}></Route>
</Switch>
```