文章目录
- 使用hook限制
- 常用 hook 函数
- 1、useState
- 2. 受控组件
- 3. useEffect
- useEffect模拟componentDidMount
- 模拟componentDidMount/componentDidUpdate
- 模拟componentDidMount/componentWillUnmount
- 模拟componentDidMount/componentDidUpdate/componentWillUnmount
- 网络请求
- 第一种写法
- 第二种写法:
- 第三种写法
- 使用useReducer完成todolist
- 5. useContext
- 6. useMemo
- 7. useCallback
- 8. React.memo
- 9. useRef
- 10. useImperativeHandle
- 11. useLayoutEffect
- 12. react-redux-hook
- 13. react-router-dom-hook
- 14. 路由懒加载和拆分
使用hook限制
常用 hook 函数
1、useState
(1)、简单实例
在函数组件中,用 useState 实现计数器功能
import React, { useState } from 'react';
function Example() {
// 声明 `count` 的 state 变量 和 用于改变 `count` 的setCount方法;
const [count, setCount] = useState(0);
// count 和 setCount 是函数内部的变量和方法,可直接访问
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
(2) 可使用多个state
function ExampleWithManyStates() {
// 声明多个 state 变量,且他们都是相互独立的
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: '学习 Hook' }]);
(3)、函数式更新
setCount( c => c +1 )
这种方式就是函数式更新,确保了 count 更新总是建立在最新的数据上,让你从 count 的管理中解脱出来。如下实例,一个计数器,count 数值的变化,永远基于最新数值,让你不用去管理count。
function Counter({ initialCount }) {
const [count, setCount] = useState(initialCount);
return (
<>
Count: {count}
<button onClick={() => setCount(initialCount)}>Reset</button>
<button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
<button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
</>
);
}
备注:此功能用在 useEffect 中,会非常方便。
(4)、惰性初始化
useState 的初始化,可以通过传入函数的方式进行,适用于复杂计算后的结果。
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});
(5)、跳过 state 更新
调用 useState 的更新方法来更新state,如果新 state 数值跟之前是一样的,那么组件会跳过子组件的渲染和 useEffect 的调用。
2. 受控组件
import React, { useState } from 'react';
const Child = () => {
let [username, setUsername] = useState('')
let [password, setPassword] = useState('')
// 登录
const dologin = () => {
console.log(username, password);
}
return (
<div>
<div>
账号:
{/* 受控组件 */}
<input type="text" value={username} onChange={e => setUsername(e.target.value.trim())} />
</div>
<div>
密码:
<input type="text" value={password} onChange={e => setPassword(e.target.value.trim())} />
</div>
<div>
<button onClick={dologin}>进入系统</button>
</div>
</div>
);
}
export default Child;
3. useEffect
useEffect模拟componentDidMount
挂载时候执行
import React, { useState, useEffect } from 'react';
// 【useEffect】它是可以在函数组件中模拟出类组件中最常用的3个生命周期方法
// 它可以被调用N次
// componentDidMount componentDidUpdate componentWillUnmount
// 【注】: useEffect中不能使用async/await,useEffect执行行完要销毁,如果你返回了一个对象,则无法销毁
const Child = () => {
let [age, setAge] = useState(10)
// 模拟componentDidMount
// 参数1:回调函数,参数2:可选项,如果为一个空数组,则模拟componentDidMount
// useEffect(()=>{},[])
useEffect(() => {
console.log('模拟componentDidMount1');
}, [])
return (
<div>
<button onClick={() => setAge(age => age + 1)}>自增年龄 -- {age}</button>
</div>
);
}
export default Child;
模拟componentDidMount/componentDidUpdate
- 只写参数1:回调函数
全局的数据更新都会触发它 - 指定state数据更新才触发,必须写参数2,依赖项
写多个参数
模拟componentDidMount/componentWillUnmount
挂载时候执行,组件销毁执行
模拟componentDidMount/componentDidUpdate/componentWillUnmount
挂载时候执行,数据更新时它会执行,组件销毁执行
网络请求
第一种写法
第二种写法:
第三种写法
import { useState, useEffect } from 'react';
import { getSwiperApi } from '@/api'
const useHttpSwiper = () => {
let [data, setData] = useState([])
const loadData = async () => {
let ret = await getSwiperApi()
setData(data => [...data, ...ret.data])
}
// useEffect它不能使用async/await
useEffect(() => {
loadData()
}, [])
return [
data,
loadData
]
}
export default useHttpSwiper
import useHttpSwiper from "@/hooks/useHttpSwiper";
const Child = () => {
let [data, loadData] = useHttpSwiper()
return (
<div>
{
data.map((item, index) => (
<li key={index}>{item.title}</li>
))
}
<hr />
<button onClick={() => loadData()}>加载更多数据</button>
</div>
);
}
export default Child;
4. useReducer
useReducer 这个 Hooks 在使用上几乎跟 Redux一模一样,唯一缺少的就是无法使用 redux 提供的中间件。
import React, { useReducer } from 'react';
// useReducer可以理解为,精简版的redux,写法和redux一致
const initState = {
count: 100
}
const reducer = (state, { type, data }) => {
if ('add' === type) return { ...state, count: state.count + data }
return state
}
const Todo = () => {
// 参数1:纯函数,用来完成state数据的修改
// 参数2:初始数据
// 参数3:惰性初始数据,如果有参数3,则参数2,无效了 回调函数,要返回一个对象
let [state, dispatch] = useReducer(reducer, initState)
return (
<div>
<h3>{state.count}</h3>
<button onClick={() => dispatch({ type: 'add', data: 2 })}>++++</button>
</div>
);
}
export default Todo;
使用useReducer完成todolist
src/App.jsx
src/Todo/reducer.js
src/Todo/index.jsx
src/Todo/ui/Todoform.jsx
src/Todo/ui/Todoitems.jsx
src/hooks/userInput.js
5. useContext
App.js
src/context/app.js
child.jsx
6. useMemo
记忆组件,可以理解为计算属性 性能优化
7. useCallback
记忆函数,它计算返回一个缓存函数。
8. React.memo
给函数组件来减少重复渲染的顶层Api,类似于PureComponent
import React from 'react';
export default class extends React.Component {
constructor(props){
super(props);
this.state = {
date : new Date()
}
}
componentDidMount(){
setInterval(()=>{
this.setState({
date:new Date()
})
},1000)
}
render(){
return (
<div>
<Child seconds={1}/>
<div>{this.state.date.toString()}</div>
</div>
)
}
}
function Child({seconds}){
console.log('I am rendering');
return (
<div>I am update every {seconds} seconds</div>
)
};
//memo 用来减少无用的重复渲染 默认只能对于普通类型进行比较
export default React.memo(Child)
React.memo()可接受2个参数,第一个参数为纯函数的组件,第二个参数用于对比props控制是否刷新,与shouldComponentUpdate()
功能类似
import React from "react";
function Child({seconds}){
console.log('I am rendering');
return (
<div>I am update every {seconds} seconds</div>
)
};
function areEqual(prevProps, nextProps) {
if(prevProps.seconds===nextProps.seconds){
return true
}else {
return false
}
}
// 针对于引用类型 进行比较
export default React.memo(Child,areEqual)
9. useRef
useRef 跟 createRef 类似,都可以用来生成对 DOM 对象的引用
useRef默认只能在html标签中使用,不可以在函数组件中使用
给自定义函数组件使用ref属性
10. useImperativeHandle
ref 的使用
普通的类组件有实例所以可以用过 React.createRef() 挂载到节点或组件上,然后通过 this 获取到该节点或组件。
class RefTest extends React.Component{
constructor(props){
super(props);
this.myRef=React.createRef();
}
componentDidMount(){
console.log(this.myRef.current);
}
render(){
return <input ref={this.myRef}/>
}
}
正常情况下 ref
是不能挂在到函数组件上的,因为函数组件没有实例,但是 useImperativeHandle
为我们提供了一个类似实例的东西。它帮助我们通过useImperativeHandle
的第 2 个参数,所返回的对象的内容挂载到 父组件的 ref.current
上。
forwardRef
会创建一个React
组件,这个组件能够将其接受的 ref
属性转发到其组件树下的另一个组件中。
import React, { forwardRef, useImperativeHandle, useEffect, useRef } from 'react'
const TestRef = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
open() {
console.log("open")
}
}))
})
function App () {
const ref = useRef()
useEffect(() => {
ref.current.open()
},[])
return(
<>
<div>111</div>
<TestRef ref={ref}></TestRef>
</>
)
}
export default App
使用useImperativeHandle可以透传 Ref,因为函数组件没有实例,所以在默认自定义函数组件中不能使用ref属性,使用为了解决此问题,react提供了一个hook和一个高阶组件完帮助函数组件能够使用ref属性。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S00ttDo3-1640594350545)(C:\Users\63062\AppData\Roaming\Typora\typora-user-images\image-20211223220537266.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zvSzUXgy-1640594350549)(C:\Users\63062\AppData\Roaming\Typora\typora-user-images\image-20211223220544407.png)]
11. useLayoutEffect
大部分情况下,使用 useEffect 就可以帮我们处理组件的副作用,但是如果想要同步调用一些副作用,比如对 DOM 的操作,就需要使用 useLayoutEffect,useLayoutEffect 中的副作用会在 DOM 更新之后同步执行。
12. react-redux-hook
react-redux支持了hook的解决方案,提供两个方法,很方便我们在项目去使用
同步
使用redux完成异步
13. react-router-dom-hook
react-router-dom也提供了hook的解决方案
import {useHistory,useLocation,useParams} from 'react-router-dom'
14. 路由懒加载和拆分
参考:
React的State Hook用法详解!