博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
如何在常见业务场景中使用React Hook
阅读量:7088 次
发布时间:2019-06-28

本文共 8392 字,大约阅读时间需要 27 分钟。

    本文将通过如何使用React Hook的API来构建react项目,摒弃传统的redux,通过使用useReducer和useContext等来实现状态分发管理,在最后会讲述如何在React Hook项目进行异步的数据请求。 Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。如何你对React Hook的概念还不是特别清楚,请移步查看这个新特性。

Provier组件

    了解redux的同学应该知道react-redux中的Provier组件,通过Provider组件可以实现将写好的store进行状态分发到下级任意一个子组件中。其实去查看Provier的实现源码,可以发现正好是使用了react的context属性,所以我们在这里同样使用React Hook的useContext属性实现状态分发。

useContext

const value = useContext(myContext)复制代码

useContext接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 <MyContext.Provider> 的 value prop 决定。

当组件上层最近的 <MyContext.Provider> 更新时,该 Hook 会触发重渲染,并使用最新传递给 MyContext provider 的 context value 值。


除此之外,还得配合上useReducer

useReducer

const [state, dispatch] = useReducer(reducer, initialArg, init);复制代码

useReducer是useState 的替代方案。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。(如果你熟悉 Redux 的话,就已经知道它如何工作了。)


有了这两项API我们就可以开始编写Provider组件了。首先创建context目录,创建好基本的reducer和index文件,mainReducer.js实现代码如下:

import mainConstants from './mainConstants';export const mainInitialState = {    error: '',    res: [],    url: '/pool/query',    loading: false,};export default (state, action) => {    switch (action.type) {        case mainConstants.INIT_PAGE:            return {...state, res: action.payload};        case mainConstants.TO_SEARCH:            return {...state, url: action.payload};        case mainConstants.PAGE_LOADING:            return {...state, loading: action.payload};        case mainConstants.CHANGE_ERROR:            return {...state, error: action.payload};        default:            return state;    }};复制代码

index.js实现代码如下:

import React, { useReducer, createContext } from 'react';import mainReducer, {mainInitialState} from './main/mainReducer';const context = createContext({});const reducer = {    main: mainReducer};// 添加状态更改的logfunction middlewareLog(lastState, action) {    const type = action.type.split('_')[0].toLowerCase();    const nextState = reducer[type](lastState, action);    console.log(        `%c|------- redux: ${action.type} -------|`,        `background: rgb(70, 70, 70); color: rgb(240, 235, 200); width:100%;`,    );    console.log('|--last:', lastState);    console.log('|--next:', nextState);    return nextState;}const Provider = props => {    const [mainState, mainDispatch] = useReducer(middlewareLog, mainInitialState);    const combined = {        main: {            state: mainState,            dispatch: mainDispatch,        },    };    return (
{props.children}
)};export {context};export default Provider;复制代码

然后在项目的主入口,加入Provier组件,实现状态分发管理,

import React from 'react';import ReactDOM from 'react-dom';import Provider from './context/';import App from './App';ReactDOM.render(
, document.getElementById('root'));复制代码

状态分发已经创建完毕,接下来是看如何在组件中获取状态和使用状态。App.js代码如下:

import React, {useContext} from 'react';import {context} from "./context";function App() {    const {state, dispatch} = useContext(context).main;    return (        
hello
);}export default App;复制代码

接下来我们设定一个业务场景,1、页面初始加载数据,2、可以根据请求参数进行重新加载数据。 根据redux的三大原则,创建action文件,代码如下:

import mainConstants from './mainConstants';export const initPage = value => ({
type: mainConstants.INIT_PAGE, payload: value});export const toSearch = value => ({
type: mainConstants.TO_SEARCH, payload: value});export const pageLoading = value => ({
type: mainConstants.PAGE_LOADING, payload: value});export const changeError = value => ({
type: mainConstants.CHANGE_ERROR, payload: value});复制代码

mainConstants代码如下:

const create = str => 'MAIN_' + str;export default {    INIT_PAGE: create('INIT_PAGE'),    TO_SEARCH: create('TO_SEARCH'),    PAGE_LOADING: create('PAGE_LOADING'),    CHANGE_ERROR: create('CHANGE_ERROR'),}复制代码

再想一下业务场景,想要在页面渲染的时候去获取数据如何做呢?根据搜索框提供的参数又如何来向接口传递呢?这边主要使用到了useEffect。

useEffect

useEffect(didUpdate);复制代码

useEffect可以让你在函数组件中执行副作用,操作数据获取,设置订阅以及手动更改 React 组件中的 DOM 都属于副作用。

如果你熟悉 React class 的生命周期函数,你可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount这三个函数的组合。

它接受两个参数,第一个参数是一个执行函数,这个执行函数是怎么处理以及什么时候执行,需要看第二个参数,一般地如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组([]),类似于生命周期中的componentDidMount。如果想要根据状态值去执行,那么只需要将状态值传入数组即可。这个API有很多规范需要探究,可以移步官方文档的进行查看。


接着改写App.js, 代码如下:

import React, {useContext, useEffect} from 'react';import {context} from "./context";import * as mainAction from "./context/main/mainAction";import {getJson} from "./util";function App() {    const {state, dispatch} = useContext(context).main;    // 设计内部变量ignore,并且在ignore为True时改变状态,    // 最后返回一个执行操作,目的在于组件卸载时,禁止修改状态    useEffect(() => {        let ignore = false;        const getData = async () => {            try {                dispatch(mainAction.pageLoading(true));                const res = await getJson(state.url);                if (!ignore) {                    dispatch(mainAction.initPage(res));                }                dispatch(mainAction.pageLoading(false));            } catch (err) {                if (!ignore) {                    dispatch(mainAction.changeError(err.message));                }            }        };        getData();        return () => {            ignore = true        };    }, [state.url, dispatch]);  // 只在url更改的时候执行    return (        
{state.res.map(item =>

{item.bagName}

)}
);}export default App;复制代码

这边是封装了一个axios的请求函数,代码如下:

import axios from 'axios';const instance = getDefaultInstance();export function getJson(url, data) {    return instance.get(url, { params: data });}function getDefaultInstance() {    const instance = axios.create({        baseURL: '/',        withCredentials: true    });    instance.interceptors.response.use(res => {        return res.data.data;    }, err => {        throw err;    });    return instance;}复制代码

官方文档中说明,希望我们将异步请求的函数直接放在useEffect中,而不是在组件内。

以上就实现了最初设定的业务场景,不过还有一个可以优化的地方。像平时工作中,几乎每个页面都会有初始请求数据和查询数据的功能,所以我们可以自定义Hook,将相同逻辑的部分实现封装。

自定义Hook

当我们想在两个函数之间共享逻辑时,我们会把它提取到第三个函数中。而组件和 Hook 都是函数,所以也同样适用这种方式。

自定义 Hook 是一个函数,其名称以 “use” 开头,而且官方规定,必须要以“use”开头,函数内部可以调用其他的 Hook

想法是自定义的hook自己管理state,所以这里用到了useState,


useState

const [state, setState] = useState(initialState);复制代码

useState是返回一个 state,以及更新 state 的函数。

在初始渲染期间,返回的状态 (state) 与传入的第一个参数 (initialState) 值相同。

setState 函数用于更新 state。它接收一个新的 state 值并将组件的一次重新渲染加入队列。

在后续的重新渲染中,useState 返回的第一个值将始终是更新后最新的 state。


改写App.js,代码如下:

import React, {useContext, useEffect, useState} from 'react';import {context} from "./context";import * as mainAction from "./context/main/mainAction";import {getJson} from "./util";function useInitPage({state, dispatch, action}) {    const [res, setRes] = useState(state.res);    const [url, setUrl] = useState(state.url);    const addValue = url => setUrl(url);    useEffect(() => {        let ignore = false;        const getData = async () => {            try {                dispatch(action.pageLoading(true));                const res = await getJson(url);                if (!ignore) {                    setRes(res); // 也可以不返回res                    dispatch(action.initPage(res));                }                dispatch(action.pageLoading(false));            } catch (err) {                if (!ignore) {                    dispatch(action.changeError(err.message));                }            }        };        getData();        return () => { ignore = true };    }, [url, dispatch]);    return {res, addValue};}function App() {    const {state, dispatch} = useContext(context).main;    const {res, addValue} = useInitPage({state, dispatch, action: mainAction});        useEffect(() => {        if (state.error !== '') {            alert(state.error);        }    }, [state.error]);    return (        
{res.map(item =>

{item.bagName}

)}
);}export default App;复制代码

总结

    本文主要通过React Hooks的几个新的API来实现了业务场景中的基础部分,实现代码略简单,概念介绍的也简单了些,不过从上手来看,还是非常实用的。class组件存在某些问题使得上手难度略高,这也是官方推荐使用React Hook的原因。官方最后公布并没有计划移除class,不过还是推荐class和Hook并行的策略。

附上

转载地址:http://ambql.baihongyu.com/

你可能感兴趣的文章
20.3. PHP_INI
查看>>
72.11. this is incompatible with sql_mode=only_full_group_by
查看>>
C# 海康DVR客户端开发系列(3)—— 连接DVR和图像预览
查看>>
为创业我做了十年的程序员,你告诉我“程序员不适合创业”?!
查看>>
mokoid android open source HAL hacking in a picture
查看>>
RCF库ClientStub.setAutoReconnect
查看>>
Google Chrome Resize Plugin
查看>>
java编程之:Unsafe类
查看>>
序列作为主键使用的原理、优缺点讨论
查看>>
iOS - AutoLayout
查看>>
如何将dubbo封装成http协议
查看>>
Android版本和API Level对应关系
查看>>
[20150806]scn headroom.txt
查看>>
使用shell脚本查看数据库负载情况
查看>>
【MOS】12c DataPump EXPORT (EXPDP) Enhancements (文档 ID 2171666.1)
查看>>
2018年,5个关于区块链趋势的基本预测
查看>>
美国洛杉矶之行
查看>>
教你用深度学习LSTM网络预测流行音乐趋势(附代码)
查看>>
JSP 内置对象Response
查看>>
nginx配置flv服务器
查看>>