react调用api等待返回结果_react-hooks & context 编写可复用react组件的一种实践

a1a448a5df663f8a3b0bb1f1eb7126f0.png

在新context API出来的时候,就已经有人提出可以用context进行状态管理。react-hooks的更新,使得这一想法的实现更为方便。以一个例子组件进行分析。

前置条件:阅读过react官方文档中,context这一 与hooks这一
中文版:context hooks

需求

我们需要实现这样一个组件:

  • 内容可变的,也就是说,具有state
  • 内容的变化是组件自己更新的,譬如:一个按钮与一段文本组成的组件,点击一下按钮,文本内容便多出一段
  • 该组件的可变内容,可以被外部组件(非子组件)获取。第二点的例子里,文本内容可以被其他组件读取到。
  • 该组件暴露内容的方式尽量简单

分析

这是很常见的需求,尤其是在jQuery时期,是一种复用性极高的组件。实现的思路也很简单:

第一种方法,按照纯react目前提供的数据流,父组件(外部组件)将方法作为该组件的props传进来。此处不提。

第二种方法,该组件有一个全局变量,保存了state。当该组件被复用时,将该组件的全局变量merge到整个APP的全局变量上。APP的其他组件通过访问全局变量来访问该组件的state。

第二种方法很容易让人想到redux等状态管理库。但实际上,context + react-hooks已经基本能满足需求。这里分享的是思路,而不是一个新lib的说明书。我也一直以为,可实践的思路远比一个实现好的库有用。

实现

全局变量选定是context。与redux相仿,为了数据流可控,这里也采用reducer接收dispatch(action),从而引起store(这里是context)变化,这样的数据流。

假设我们要实现的组件叫Editor

定义action

Editor/actionTypes.js

export const ADD_PARA = 'ADD_PARA'

Editor/actions.js

import { ADD_PARA } from './actionTypes'

export const addPara = para => ({
    type: ADD_PARA,
    para,
})

编写reducer

Editor/reducer.js

import { ADD_PARA } from './actionTypes'
import { composeReducer } from '../utils'

const reducer1 = (state, action) => {
    switch (action.type) {
        case ADD_PARA: {
            return [action.para, ...state]
        }
        default: {
            return state
        }
    }
}

const reducer = composeReducer(reducer1) 
//自己实现一个工具方法,可以合并reducer,当然这里只有一个reducer

export default reducer

hooks实现context的api

Editor/context.js

这个文件里,我们编写需要暴露给外部的api。因为依赖context,实际上能够暴露出去的只有三个api:ProviderConsumerContext

作为createContext()返回的Context对象,本身具有ProviderConsumer两个属性。Providervalue属性的值应为context的初始值,而且必须在此设值。

这里有一个小小的坑点; createContext()方法的参数,并不是初始值,其参数仅仅是为了在 Provider不存在的情况下初始化context,英文文档这里写得比较明确。所以实际意义上的 initValue应该在 Providervalue属性设值。
The defaultValue argument is only used when a component does not have a matching Provider above it in the tree. This can be helpful for testing components in isolation without wrapping them. Note: passing undefined as a Provider value does not cause consuming components to use defaultValue.

useReducer方法可以基于给定reducer返回dispatch方法

全局变量的值与改变值的方法一并作为context传入。这样使得Editor组件本身可以改变自己的state

基于以上原因与需求第四点,如下实现:

import React, { createContext, useReducer } from 'react'

import reducer from './reducer'

const EditorContext = createContext()
const initContext = []

//封装一下Provider,为其设初始值
const EditorProvider = props => {
    const [state, dispatch] = useReducer(reducer, initContext)
    return (
        <EditorContext.Provider value={{ state, dispatch }}>
            {props.children}
        </EditorContext.Provider>
    )
}

const EditorConsumer = EditorContext.Consumer

export { EditorContext, EditorConsumer, EditorProvider }

view

Editor/view.js

实现逻辑非常简单,所以这里暂不区分容器组件与傻瓜组件

useContext方法可以获取context的值,并在context变动时重新渲染

import React, { useContext } from 'react'

import { EditorContext } from './context'
import { addPara } from './actions'

const Editor = props => {
    const { state, dispatch } = useContext(EditorContext)

    return (
        <>
            {state.map(content => (
                <p key={content + Math.random()}>{content}</p>
            ))}
            <button onClick={() => dispatch(addPara('gggg'))}>add</button>
        </>
    )
}

export { Editor }

导出

Editor/index.js

export { Editor } from './view'
export { EditorContext, EditorProvider, EditorConsumer } from './context'

这样,一个满足要求的Editor组件就实现完成了,该组件由一个文本区域与一个button组成,每次点击button,文本区域会增加一个值为ggggp段落

调用

App.js

调用该组件,实际上是把导出的Provider作为共同父组件即可

import React from 'react'

import { Editor, EditorProvider } from './Editor'
import Exp from './Exp'
import './App.css'

const App = props => (
    <EditorProvider className="App">
        <Editor />
        <Exp />
    </EditorProvider>
)

export default App

Exp/index.js

import React, { useContext } from 'react'

import { EditorContext } from '../Editor'

const Exp = () => {
    const { state } = useContext(EditorContext)

    return (
        <>
            {state.map(content => (
                <p key={content + Math.random()}>{content}</p>
            ))}
        </>
    )
}

export default Exp

如上,我们把另一个需要访问该组件值的组件Exp与该组件Editor的共同父组件作为ProviderExp可以很方便的访问Editorstate


以上就使用react-hooks与context完成了一个简单的,易复用的react组件。

实现该组件的目的,不仅仅是满足需求,也是为了看到hooks与context相结合,作状态管理的潜力。

友情提示:
目前(2019.02.03),能够使用react-hooks的react版本 "react": "^16.8.0-alpha.1", "react-dom": "^16.8.0-alpha.1"
更多内容,可以参考Facebook的redux-react-hook:它是另一种思路,保留redux,用context与hooks代替react-redux。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值