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. 组件分为类组件和函数组件,以下是有状态组件即类组件
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
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 的简单使用
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
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 共享状态钩子,上下文
// 该钩子的作用是,在组件之间共享状态。关于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():副作用钩子
/*熟悉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
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
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代码如下:
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

浙公网安备 33010602011771号