个人学习笔记
ReactHooks介绍
React Hooks是用来做什么的
- 对函数型组件进行增强,让函数组件可以存储状态,可以拥有副作用的能力,让开发者在不使用类组件的情况下,实现相同的功能
类组件的不足(Hooks)要解决的问题
- 缺少逻辑复用机制,为了复用逻辑增加无实际渲染效果的组件,增加了组件层级,显示十分臃肿,增加了调试的难度以及运行效率低
- 类组件经常会变得很复杂难以维护,将一组相干的业务逻辑拆分到多个生命周期函数中,在一个生命周期函数内存在多个不相干的业务逻辑
- 类成员方法不能保证this指向的正确性
- Hooks 意为钩子,React Hooks 就是一堆钩子函数,React通过这个钩子函数对函数型组件进行增强,不同的钩子函数提供了不同的功能
一:useState
import React, { useState } from 'react'
// 第一个参数是保存的状态,第二个参数是设置状态的方法
const [count, setCount] = useState(0)
二:useState 细节
- 接收唯一的参数既状态的初始值,初始值可以是任意数据类型
- 返回值为数组,数组中存储状态值和更改状态值的方法,方法名称约定以set开头,后面接状态名称
- 方法可以被调用多次,用于保存不同状态值
- 参数可以是一个函数,函数返回什么,初始值就是什么,函数只会被调用一次,用在初始值是动态值的情况
- 设置状态值方法的参数可以是一个值也可以是一个函数
- 设置状态值方法的方法本身是异步的
import React, { useState } from 'react'
const [person, setPerson] = useState({
name: '123',
age: 18
})
// 只想设置一个
setPerson({
...person,
name: '456'
})
import React, { useState } from 'react'
const [count, setCount] = useState(0)
// 1.可以传递一个函数
setCount(() => {
return 100
})
setCount((count) => {
return count + 1
})
// 2.方法本身是异步的
setCount((count) => {
let cnt = count + 1
// 执行这里是是 1
document.title = cnt
return cnt
})
// 执行这里是count依然是 0
document.title = count
三:useReducer
- useReducer 是另一种让函数组件保存状态的方式
- 值被保存到了一个特定的地方,组件想要更改状态,组件需要通过dispatch方法去触发一个action
import React, { useReducer } from 'react'
const App = () => {
const reducer = (state, action) => {
switch (action.type) {
case 'increment':
return state + 1
case 'decrement':
return state - 1
}
}
// 第一个参数就是上面的方法
// 第二个参数是 count 的值
const [count, dispatch] = useReducer(reducer, 0)
return (
<div>
<button
onClick={() => {
dispatch({ type: 'increment' })
}}
>
+1
</button>
<span>{count}</span>
<button
onClick={() => {
dispatch({ type: 'decrement' })
}}
>
-1
</button>
</div>
)
}
export default App
四:useContext
- 在跨组件层级获取数据时简化获取数据的代码
import { createContext, useContext } from 'react'
const countContext = createContext()
function App() {
return (
<countContext.provider value={100}>
<Foo />
</countContext.provider>
)
}
export default App
// 获取传递值,没有简化的版本
function Foo() {
return (
<countContext.Consumer>
{(value) => {
return <div>{value}</div>
}}
</countContext.Consumer>
)
}
// 使用useContext的简化版本
function Foo() {
const count = useContext(countContext)
return <div>{count}</div>
}
五:useEffect钩子函数执行时机分析
- 让函数类型组件拥有处理副作用的能力,类似生命周期函数
- 可以把 useEffect 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这个三个函数的组合
- useEffect(()=>{}) => componentDidMount, componentDidUpdate
- useEffect(()=>{},[]) => componentDidMount
- useEffect(()=> ()=>{}) =>componentWillUnmount
import React, { useEffect, useState } from 'react'
const App = () => {
const [count, setCount] = useState(0)
// 会在组件挂载完成之后执行 组件数据更新完成之后执行
useEffect(() => {
console.log('123')
})
// 只在组件挂载完成执行
useEffect(() => {
console.log('456')
}, [])
// 组件卸载之前执行
useEffect(() => {
return () => {
console.log('789')
}
})
return (
<div>
<span>{count}</span>
<button
onClick={() => {
setCount(count + 1)
}}
>
+1
</button>
</div>
)
}
六:useEffect的使用方式
import React, { useEffect, useState } from 'react'
const App = () => {
const onScroll = () => {
console.log('页面发生了滚动')
}
useEffect(() => {
window.addEventListener('scroll', onScroll)
return () => {
window.removeEventListener('scroll', onScroll)
}
}, [])
const [count, setCount] = useState(0)
useEffect(() => {
const timerId = setInterval(() => {
setCount((count) => count + 1)
}, 1000)
return () => {
clearInterval(timerId)
}
}, [])
// useEffect 的第二个参数
// 只有指定数据发生变化时触发effect
useEffect(() => {
console.log('count数值发生改变')
}, [count])
// useEffect 钩子函数结合异步函数
// useEffect中的参数函数不能是异步函数,因为useEffect函数要返回清理资源的函数,如果是异步函数就变成了返回Promise
useEffect(async () => {
let data = await a()
}, [])
// 使用这种方法可以是使用
useEffect(() => {
;(async () => {
let a = await getData()
})()
}, [])
return <div>aa</div>
}
export default App
七:useMemo
- useMemoe 的行为类似 Vue 中的计算属性,可以检测某个值的变化,根据变化值计算新值
- useMemo 会缓存计算结果,如果检测值没有发生变化,即使组件重新渲染,也不会重新计算,此行为可以有助于避免在每个渲染上进行昂贵的计算
import React, { useState, useMemo } from 'react'
const App = () => {
const [count, setCount] = useState(0)
const result = useMemo(() => {
// 如果 count 值发生变化此函数重新执行,并且可以在视图上渲染
return count * 2
}, [count])
return (
<div>
{count} {result}
</div>
)
}
export default App
八:memo
- 使用memo方法提高组件性能
- 如果本组件中的数据没有发生变化,阻止组件更新,类似组件中的PureComponent 和 shouldComponentUpdate
import React, { memo } from 'react'
const App = () => {
const [count, setCount] = useState(0)
return (
<div>
<div>APP</div>
<button
onClick={() => {
setCount(count + 1)
}}
>
+1
</button>
<Foo />
</div>
)
}
// count发生改变 Foo不需要重新渲染,减少不必要的渲染
const Foo = memo(() => {
console.log('使用memo后,count发生变化,这个组件不会重新渲染')
return (
<div>
<div>Foo</div>
</div>
)
})
export default App
九:useCallback
- 性能优化,缓存函数,使组件重新渲染时得到相同的函数实例
import React, { useState, useCallback } from 'react'
const App = () => {
const [count, setCount] = useState(0)
// 使用useCallback 后在Foo组件调用resetCount方法,Foo不会重新渲染
// 没有使用则重新渲染
const resetCount = useCallback(() => {
setCount(0)
}, [setCount])
return (
<div>
<div>{count}</div>
<Foo resetCount={resetCount} />
</div>
)
}
// count发生改变 Foo不需要重新渲染,减少不必要的渲染
const Foo = memo((props) => {
return (
<div>
<button onClick={props.resetCount}>resetCount</button>
</div>
)
})
export default App
十:useRef
- 获取DOM元素
- 保存数据(跨组件周期)即使组件重新渲染,保存的数据仍然存在,保存的数据更改不会触发组件重新渲染
import React, { useRef, useState, useEffect } from 'react'
const APP = () => {
const [count, setCount] = useState(0)
const username = useRef()
// 可以获取到一个 current { current: input }
const handle = () => console.log(username)
// 保存数据,组件重新渲染时,数据依旧存在,数据不会丢失
let timerId = useRef()
useEffect(() => {
timerId.current = setInterval(() => {
setCount((count) => count + 1)
}, 1000)
}, [])
const stopCount = () => {
clearInterval(timerId.current)
}
return (
<div>
<input ref={username} onChange={handler} />
<div>{count}</div>
<button onClick={stopCount}>停止</button>
</div>
)
}
export default APP
十一:自定义hookH函数
- 自定义 Hook 是标准的封装和共享逻辑的方式
- 自定义 Hook 是一个函数,其名称以 use 开头
- 自定义 Hook 其实就是逻辑和内置 Hook 的组合
import React, { useEffect, useState } from 'react'
import axios from 'axios'
useGetPost = () => {
const [post, setPost] = useState
useEffect(() => {
axios.get('url').then((res) => setPost(res.data))
}, [])
return [post, setPost]
}
const APP = () => {
const [post, setPost] = useGetPost()
return (
<div>
<div>自定义hook函数</div>
</div>
)
}
export default APP
import React, { useState } from 'react'
useUpdateInput = (initialValue) => {
const [value, setValue] = useState(initialValue)
return {
value,
onChange: (event) => setValue(event.target.value),
}
}
const App = () => {
const usernameInput = useUpdateInput('')
const usewordInput = useUpdateInput('')
const submitForm = (event) => {
event.perventDefault()
console.log(usernameInput.value)
console.log(usewordInput.value)
}
return (
<form onSubmit={submitForm}>
<input type="text" name="username" {...usernameInput} />
<input type="password" name="password" {...usewordInput} />
<input type="submit" />
</form>
)
}
export default App
十二:路由Hook
- react-router-dom 路由提供的钩子函数
- useHistory,useLocation,useRouterMatch,useParams