一、hook
的认识
Hook
是react16.8
的新增特性。它可以让你在不编写class
的情况下使用state
的特性,换句话说就是你使用函数组件也可以使用state
等属性
- 有了
hook
,你再也不用写class
组件了,所有的组件都可以是函数组件 - 有了
hook
,生命周期钩子函数可以丢一边了,函数组件中不需要考虑生命周期钩子函数
二、state
的使用
-
state
是状态属性,与之对应的是setState
是设置状态属性的,一般情况都是成对的出现 -
定义方式
const [属性名称,set属性名称] = useState(初始化值); // 比如下面的定义 const [age, setAge] = useState(10); const [books, setBooks] = useState(['西游记', '红楼梦']); const [address, setAddress] = useState({ province: '广东省', city: '深圳市', }); const [isLogin, setIsLogin] = useState(false);
-
使用和修改状态值
const addBook = () => { setBooks([...books, '水浒传']); } return ( <> 测试案例 <div onClick={() => setAge(age+1)}>{age}</div> { books.map(item => ( <li key={item}>{item}</li> )) } <button onClick={addBook}>添加书籍</button> </> );
三、useEffect
的使用
-
1、基本的使用
在页面加载完成后会执行一次,不管是修改
age
、isLogin
的时候都会执行useEffect
函数import { useState, useEffect } from 'react'; const HookDemo = () => { const [age, setAge] = useState(10); const [isLogin, setIsLogin] = useState(false); useEffect(() => { console.log('useEffect执行了'); }); return ( <> 测试案例 <div onClick={() => setAge(age + 1)}>{age}</div> <div onClick={() => setIsLogin(!isLogin)}>{isLogin}==</div> </> ); } export default HookDemo;
-
2、如果要修改只监听一个属性的时候才执行,需要添加第二个参数(第二个参数是一个数组,可以是监听多个属性)
// 页面加载的时候会执行一次,当只改名age的时候会执行,改变isLogin的时候是不会执行的 useEffect(() => { console.log('useEffect执行了'); },[age]);
-
3、在
useEffect
的回调函数中是不能使用async
,如果一定要使用async
的话,那么可以采用下面两种方式-
直接在
useEffect
中使用useEffect(() => { console.log('useEffect执行了'); const demo = async () => { console.log('异步函数'); } demo(); },[age]);
-
将异步函数直接提到
useEffect
外面来const demo = async () => { console.log('异步函数'); }; useEffect(() => { console.log('useEffect执行了'); demo(); },[age]);
-
-
4、如果页面关闭的时候要触发事件就直接写在返回函数中就可以,比如关闭定时器,解绑
dom
事件useEffect(() => { console.log('useEffect执行了'); return () => { console.log('关闭页面的时候执行'); } },[age]); const handleGoHome = () => { history.push({ pathname: '/user', }); }
四、useLayoutEffect
的使用
- 基本上90%的时候都是用
useEffect
useEffect
是在render
渲染结束后useLayoutEffect
是在DOM
渲染后,如果你要操作DOM
建议你在这个里面操作
五、useMemo
的使用
useMemo
其实就是对属性的缓存,当别的属性改变的时候改属性的不渲染,如果不使用的情况下就会一起渲染
-
不使用
useMemo
的时候import { useState, useEffect, useMemo } from 'react'; const HookDemo = () => { const [age, setAge] = useState(10); const [isLogin, setIsLogin] = useState(false); // 当点击isLogin改变的时候这个函数也会执行 const noCacheAge = () => { console.log('age改变函数执行了'); return age; } return ( <> 测试案例 <div onClick={() => setAge(age + 1)}>{age}</div> <div onClick={() => setIsLogin(!isLogin)}>{isLogin}==</div> <div>{noCacheAge()}</div> </> ); } export default HookDemo;
-
使用
useMemo
改造上面的属性import { useState, useEffect, useMemo } from 'react'; const HookDemo = () => { const [age, setAge] = useState(10); const [isLogin, setIsLogin] = useState(false); const noCacheAge = () => { console.log('age改变函数执行了'); return age; } const cacheAge = useMemo(() => { console.log('缓存的函数属性改变'); return age; }, [age]); return ( <> 测试案例 <div onClick={() => setAge(age + 1)}>{age}</div> <div onClick={() => setIsLogin(!isLogin)}>{isLogin}==</div> <div>{noCacheAge()}</div> <div>{cacheAge}</div> </> ); } export default HookDemo;
六、useCallback
的使用
useCallback
其实和useMemo
类似,只是一个缓存属性一个缓存方法的,使用方式也类似
const handleAge = useCallback(() => {
console.log('age');
setAge(age + 1);
}, [age])
七、useReducer
的使用
useReducer
的使用与上面的hook
来说相对复杂点,不是一行代码,一句话就可以解决的问题
-
1、文件结构
. ├── app.js ├── index.js ├── useContext.js └── user.js 0 directories, 4 files
index.js
入口组件app.js
渲染组件user.js
用户组件useContext.js
定义useReducer
-
2、
useReducer.js
文件import React, { useReducer } from 'react'; const initState = { isLogin: false, userInfo: { id: 1, name: '哈哈' } } const reducer = (state, action) => { switch(action.type) { case 'LOGIN': return { ...state, isLogin: action.payload, } default: return state; } } export const UserContext = React.createContext(); export const UserContextProvider = (props) => { const [state, dispatch] = useReducer(reducer, initState); return ( <UserContext.Provider value={{state, dispatch}}> {props.children} </UserContext.Provider> ) }
-
3、
index.js
文件内容import { UserContextProvider } from './useContext'; import App from './app'; const HookDemo = (props) => { return ( <UserContextProvider> <App {...props}/> </UserContextProvider> ); } export default HookDemo;
-
4、
app.js
文件内容import { useContext } from 'react'; import { UserContext } from './useContext'; import User from './user'; const App = () => { const {state, dispatch} = useContext(UserContext); console.log(state, '?'); const handleLogin = () => { dispatch({ type: 'LOGIN', payload: true, }) } return ( <div> {state.isLogin ? <User /> : <button onClick={handleLogin}>登录</button>} </div> ); } export default App;
-
5、
user.js
文件import { useContext } from 'react'; import { UserContext } from './useContext'; const User = () => { const {state} = useContext(UserContext); return ( <div> 用户组件 <div> 名字: {state.userInfo.name} </div> </div> ); } export default User;
八、自定义hook
(修改页面标题的hook
)
-
1、在项目根目录中创建一个文件夹
hooks
-
2、创建一个文件
hooks/useTitleHook.js
import {useLayoutEffect, useState} from 'react'; export const useTitleHook = (title) => { const [state, setState] = useState(); useLayoutEffect(() => { document.title = title; setState(title); }, [title]); return state; }
-
3、使用自定义的
hooks
const HookDemo = (props) => { const title = useTitleHook('测试页面'); return ( <UserContextProvider> <App {...props}/> <div>{title}</div> </UserContextProvider> ); } export default HookDemo;
九、自定义hook
(封装一个ajax
的请求)
-
1、在
hooks
文件夹下创建一个useHttpHook.js
的文件import {useState, useEffect} from 'react'; import {http} from '@/utils'; export const useHttpHook = ({url, method='get', headers={}, body = {}, watch = []}) => { // 结果集 const [result, setResult] = useState(); // 是否已经请求完成 const [loading, setLoading] = useState(true); useEffect(() => { http({url,method, headers, body, setResult, setLoading}); },watch); return [result, loading]; }
-
2、关于
utils/http
方法的定义import { Toast } from 'antd-mobile'; const baseUrl = '/api'; export const http = ({ url, method = 'get', headers = {}, body = {}, setLoading, setResult, }) => { setLoading && setLoading(true); const defaultHeader = { 'Content-type': 'application/json' }; let params; if (method.toUpperCase() === 'GET') { params = undefined; } else { params = { headers: { ...defaultHeader, headers }, method, body: JSON.stringify(body) } } return new Promise((resolve,reject) => { fetch(baseUrl + url, params) .then(res => res.json()) .then(res => { if (res.status === 200) { resolve(res.data); // 成功了将数据设置到result中 setResult && setResult(res.data); } else { Toast.fail(res.errMsg); reject(res.errMsg); } }).catch(err => { Toast.fail(err); reject(err); }).finally(()=> { setLoading && setLoading(false); }) }) }
-
3、使用自定义的
hook
请求数据const [houses, housesLoading] = useHttpHook({ url: '/house/hot', }); // 请求出来的结果在houses中,是否请求完成根据housesLoading来判断 // 至于houses和housesLoading是自己随便定义的语义化名称
十、react
项目中结合think-react-store
的使用
-
2、基本的使用可以参考官方文档
-
3、本人给出简单的使用方式加上标注
-
基本的目录结构
. ├── index.js ├── stores │ ├── index.js │ └── user.js └── user.js 1 directory, 4 files
-
stores/user.js
文件代码export default { // 状态 state: { id: 123, name: 'cc' }, // 同步方法 reducers: { setName(state, payload) { return { ...state, ...payload } } }, // 异步方法 effects: { async setNameAsync(dispatch, rootState, payload) { await new Promise(resolve => { setTimeout(() => { resolve(); }, 1000) }) // 调用reducers里面的方法 dispatch({ type: 'setName', // type是指reducers里面的方法名,你想调用那个就用那个的方法名 payload }) } } }
-
stores/index.js
文件代码export { default as user } from './user';
-
index.js
代码(入口函数)import * as store from './stores'; import {StoreProvider} from 'think-react-store'; import User from './user'; const StoreDemo = () => { return ( <StoreProvider store={store}> <User/> </StoreProvider> ); } export default StoreDemo;
-
user.js
应用组件import { useStoreHook } from 'think-react-store'; const User = (props) => { const { user: { id, name, setNameAsync, setName } } = useStoreHook(); console.log(setNameAsync, '==>', name, id, setName); const handleChange1 = () => { setName({id: 10, name: '哈哈'}); } const handleChange2 = () => { setTimeout(() => { setNameAsync({id: 100, name: '李四'}) }, 1000); } return ( <div> <div>用户ID: {id}</div> <div>用户名:{name}</div> <button onClick={handleChange1}>同步修改</button> <button onClick={handleChange2}>异步修改</button> </div> ); } export default User;
-
-
4、如果要配置中间件的话直接在入口中配置
import * as store from './stores'; import {StoreProvider} from 'think-react-store'; import log from 'think-react-store/middlewares/log'; import User from './user'; const StoreDemo = () => { return ( <StoreProvider store={store} middleware={[log]}> <User/> </StoreProvider> ); } export default StoreDemo;