React Hooks 知识
一. 为什么要使用React Hooks?why
1. 为了解决 组件之间很难重用有状态逻辑/复杂的组件变得难以理解/无法重用class
2. 介绍Hooks之前,首先要给大家说一下React的组件创建方式,一种是类组件,一种是纯函数组件,并且React团队希望,组件不要变成复杂的容器,最好只是数据流的管道。开发者根据需要,组合管道即可。也就是说组件的最佳写法应该是函数,而不是类。。
但是我们知道,在以往开发中类组件和纯函数组件的区别是很大的,纯函数组件有着类组件不具备的多种特点,简单列举几条
- 纯函数组件没有状态
- 纯函数组件没有生命周期
- 纯函数组件没有
this
- 只能是纯函数
这就注定,我们所推崇的函数组件,只能做UI展示的功能,涉及到状态的管理与切换,我们不得不用类组件或者redux,但我们知道类组件的也是有缺点的,比如,遇到简单的页面,你的代码会显得很重,并且每创建一个类组件,都要去继承一个React实例,至于Redux,更不用多说,很久之前Redux的作者就说过,“能用React解决的问题就不用Redux”,等等一系列的话。关于React类组件redux的作者又有话说
- 大型组件很难拆分和重构,也很难测试。
- 业务逻辑分散在组件的各个方法之中,导致重复逻辑或关联逻辑。
- 组件类引入了复杂的编程模式,比如 render props 和高阶组件。
二. React Hooks是什么,有什么优缺点?what
React Hooks 的意思是,组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码"钩"进来
三. 怎样更好的理解与在项目使用React Hooks? how
使用常用react钩子来更好使用
- useState()
- userContext()
- userReducer()
- useEffect()
1. 组件分为类组件和函数组件,以下是有状态组件即类组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | import React from 'react' ; class Example extends React.Component { constructor(props) { super (props) this .state = { count: 0 } } render () { return ( <div> <p>You clicked { this .state.count}</p> <button onClick={() => this .setState({count: this .state.count + 1 })}>Click Me</button> </div> ) } } export default Example |
二. 常用的React Hooks 钩子
React Hooks : useState 状态组件/useReducer扩展,代替Redux 扩展组件/
1. useState
1 2 3 4 5 6 7 8 9 10 11 | import React, { useState } from 'react' ; function FuncUseCount (){ const [count, setCount] = useState(0); return ( <div>Use state <p>You clicked {count}</p> <button onClick={() => setCount(count + 1 )}>Click Me</button> </div> ) } export default FuncUseCount |
2. useReducer
2.1 useReducer 的简单使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | import React, { useReducer } from 'react' /* 先用dispatch 慢慢研究 一、创建初始值initialState 二、创建所有操作reducer(state, action); 三、传给userReducer,得到读和写API 四、调用写({type: '操作类型'}) 总的来说,useReducer 是 useState 的复杂版 链接:https://www.jianshu.com/p/1252be39c702 */ const initial = {age: 0} const reducer = (state, action) => { if (action.type === 'add' ) { return {age: state.age + action.number} } else if (action.type === 'multi' ) { return {age: state.age * 2} } else { throw new Error( 'unknown type' ) } }; function HootReducer() { const [state, dispatch] = useReducer(reducer, initial); const { age } = state const onClick1 = () => { dispatch({ type: 'add' , number: 1}); }; const onClick2 = () => { dispatch({ type: 'add' , number: 2}); } return ( <div> <p>{age}</p> <button onClick={onClick1}>按钮+1</button> <button onClick={onClick2}>按钮+2</button> </div> ) } export default HootReducer |
2.2 useReducer扩展,代替Redux
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | import React, { useReducer, useContext, useEffect } from "react" ; const Store = { user: null , books: null , movies: null } function Reducer(state, action) { switch (action.type) { case 'setUser' : return { ...state, user: action.user }; case 'setBooks' : return { ...state, books: action.books }; case 'setMovies' : return { ...state, movies: action.movies }; default : throw new Error( 'error action type' ) } } // 上下文,共享字段 const Context = React.createContext( null ); function Redux() { const [state, dispatch] = useReducer(Reducer, Store) const api = { state, dispatch }; return ( <Context.Provider value={api}> <User/> <Books/> <Movies/> </Context.Provider> ) } function User() { const { state, dispatch } = useContext(Context) useEffect(() => { ajax( '/user' ).then(user => { dispatch({ type: 'setUser' , user: user}) }) },[]); return ( <div> <h1>个人信息</h1> <div>name: { state.user ? state.user.name : '' }</div> </div> ) } function Books() { const { state, dispatch } = useContext(Context); useEffect(() => { ajax( '/books' ).then(books => { dispatch({ type: "setBooks" , books: books }); }); }, []); return ( <div> <h1>我的书籍</h1> <ul> {state.books ? state.books.map(book => <li key={book.id}>{book.name}</li>) : "加载中" } </ul> </div> ); } function Movies() { const { state, dispatch } = useContext(Context); useEffect(() => { ajax( '/movies' ).then(movies => { dispatch({ type: "setMovies" , movies: movies }); }); }, []); return ( <div> <h1>我的电影</h1> <ul> {state.movies ? state.movies.map(movie => <li key={movie.id}>{movie.name}</li>) : "加载中" } </ul> </div> ); } function ajax(path) { return new Promise((resolve, reject) => { setTimeout(() => { switch (path) { case '/user' : { resolve({ id: 1, name: "Frank" }); } break ; case '/books' : { resolve([ { id: 1, name: "JavaScript 高级程序设计" }, { id: 2, name: "JavaScript 精粹" } ]); } break ; case '/movies' : { resolve([ { id: 1, name: "爱在黎明破晓前" }, { id: 2, name: "恋恋笔记本" } ]); } break ; default : break ; } }, 1500); }) } export default Redux |
3. useContext 共享状态钩子,上下文
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | // 该钩子的作用是,在组件之间共享状态。关于Context这里不再赘述,其作用就是可以做状态的分发,在React16.X以后支持,避免了react逐层通过Props传递数据。 // 下面是一个例子,现在假设有A组件和B组件需要共享一个状态。 import React, { useContext } from 'react' function conTextTest () { const AppContext = React.createContext({}) const A = () => { const { name, title } = useContext(AppContext) return ( <div>A组件的数据是: {name}-{title}</div> ) } const B = () => { const { name, title } = useContext(AppContext) return ( <div>B组件的数据是: {name}-{title}</div> ) } return ( <AppContext.Provider value={{name: 'Hoots共享状态钩子' , title: 'useContext' }}> <A/> <B/> </AppContext.Provider> ) } export default conTextTest |
4. useEffect():副作用钩子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | /*熟悉redux-saga的同学一定对Effect不陌生,它可以用来更好的处理副作用, 如异步请求等,我们的useEffect()也是为函数组件提供了处理副作用的钩子。 依然我们会把请求到componentDidMount里面,在函数组件中我们可以使用useEffect()。其具体用法如下 */ // useEffect(() => {},[array]) /*useEffect()接受两个参数,第一个参数是你要进行的异步操作, 第二个参数是一个数组,用来给出Effect的依赖项。只要这个数组发生变化, useEffect()就会执行。当第二项省略不填时,useEffect()会在每次组件渲染时执行。 这一点类似于类组件的componentDidMount。下面我们通过代码模拟一个异步加载数据。 */ import React, { useState, useEffect } from 'react' const AsyncPage = () => { const [loading, setLoading] = useState( true ) useEffect(() => { setTimeout(() => { setLoading( false ) }, 3000); }) return ( loading ? <p>Loading...</p> : <p>异步请求完成</p> ) } export default AsyncPage |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | import React, { useState, useEffect } from 'react' const AsyncPage = ({ name }) => { const [loading, setLoading] = useState( true ) const [person, setPerson] = useState({}) useEffect(() => { setTimeout(() => { setLoading( false ) setPerson({name}) }, 500); }) return ( loading ? <p>Loading...</p> : <p>{ person.name }</p> ) } const PersonPage = () => { const [state, setState] = useState( '我的名字' ) const changeName = (name) => { setState(name) } return ( <div> <AsyncPage name={state} /> <button onClick={() => {changeName( '张三' )}}>张三</button> <br/> <button onClick={() => {changeName( '李四' )}}>李四</button> </div> ) } export default PersonPage |
5. 自定义Hooks
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | import React, { useState, useEffect } from 'react' const usePerson = (name) => { const [loading, setLoading] = useState( true ) const [person, setPerson] = useState({}) useEffect(() => { setLoading( true ) setTimeout(() => { setLoading( false ) setPerson({name}) }, 500); }, [name]) return [loading, person] } const AsyncPages = ({name}) => { const [loading, person] = usePerson(name) return ( <div> { loading ? <p>loading...</p> : <p>{ person.name }</p>} </div> ) } const PersonsPage = () => { const [state, setState] = useState( '' ) const changeName = (name) => { setState(name) } return ( <div> <AsyncPages name={state} /> <button onClick={() => {changeName( '名字1' )}}>名字1</button> <button onClick={() => {changeName( '名字2' )}}>名字2</button> </div> ) } export default PersonsPage |
App.js代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | import './App.css' ; // import PropsList from './Props/List' import HootsState from './Hoots/useState' import HootsContext from './Hoots/useContext' import HootsReducer from './Hoots/useReducer' // import HootsEffect from './Hoots/useEffect' import HootsEffect2 from './Hoots/useEffect2' import HootsCustom from './Hoots/customHooks' function App() { return ( <div className= "App" > 购物车 { /* <PropsList /> */ } <HootsState /> <HootsContext /> <HootsReducer /> { /* <HootsEffect /> */ } <HootsEffect2 /> <HootsCustom /> </div> ); } export default App; |
以上只供学习用
参考文章: https://www.jianshu.com/p/1252be39c702
https://www.jianshu.com/p/d600f749bb19
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
2017-02-18 swift 综合练习之通迅录