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
// 上面的代码实现了一个异步加载,下面我们再做一个useEffect()依赖第二项数组变化的例子。
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

以上我们介绍了四种最常用的react提供给我们的默认React Hooks,有时候我们需要创建我们自己想要的Hooks,来满足更便捷的开发,在小编看来,无非就是根据业务场景对以上四种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

posted @   TheYouth  阅读(61)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
历史上的今天:
2017-02-18 swift 综合练习之通迅录
点击右上角即可分享
微信分享提示