Redux, сделай основные действия многократного использования, чтобы уменьшить шаблон кода - PullRequest
0 голосов
/ 31 августа 2018

Недавно начал работать с React + Redux, и поэтому у меня может возникнуть неправильное ощущение, но мне кажется, что в некоторых случаях действия можно сделать многоразовыми для всего приложения.

В нашем магазине приложений мы обычно устанавливаем следующие типы данных:

  • строка
  • булево
  • массив
  • объект
  • номер

Я решил создать помощников действий, которые будут работать вместе с константами. Ниже приведена реализация. Обратите внимание, что для этого требуется использовать промежуточное программное обеспечение redux-thunk.

//baseActions.js

function baseType(dispatch, payloadType, type, getState) {
    return async (initialPayload, transformer) => {
        var realPayloadType;
        switch (payloadType) {
            case 'array' :
                realPayloadType = Array.isArray(initialPayload) ? 'array' : null
                break
            default:
                realPayloadType = typeof initialPayload
        }

        if (realPayloadType !== payloadType && initialPayload !== null)
            throw new Error(`Payload should be ${payloadType}, but ${realPayloadType} provided`)

        const payload = transformer ? await transformer({payload: initialPayload, dispatch, getState}) : initialPayload

        return dispatch({
            type,
            payload
        })
    }
}

export function setNumber(actionType) {
    return (dispatch, getState) => {
        return baseType(dispatch, 'number', actionType, getState)
    }
}

export function setBoolean(actionType) {
    return (dispatch, getState) => {
        return baseType(dispatch, 'boolean', actionType, getState)
    }
}

export function setString(actionType) {
    return (dispatch, getState) => {
        return baseType(dispatch, 'string', actionType, getState)
    }
}

export function setArray(actionType) {
    return (dispatch, getState) => {
        return baseType(dispatch, 'array', actionType, getState)
    }

}

export function setObject(actionType) {
    return (dispatch, getState) => {
        return baseType(dispatch, 'object', actionType, getState)
    }
}

Следует отметить, что методы, помимо полезной нагрузки, также могут принимать функцию transformer , которая может использоваться для асинхронных действий. Я покажу пример в компоненте позже.

Теперь мы можем импортировать этот файл в наш контейнер:

import React, { Component } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import User from '../components/User'
import {SET_USER_NAME} from '../constants/userConstants'
import * as actions from '../shared/actions/baseActions'

//App container

class App extends Component {
    render() {
        const { user } = this.props;
        const { setString } = this.props.actions

        return <User setName={setString(SET_USER_NAME)} name={user.first_name} avatar={user.avatar} />

    }
}

function mapStateToProps (state) {
    return {
        user: state.user
    }
}

function mapDispatchToProps(dispatch) {
    return {
        actions: bindActionCreators(actions, dispatch)
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(App)

Я импортирую константы в контейнер, а не в действие, как это обычно делается. Создатели базовых действий не должны знать типы действий, поэтому вы можете передавать те, которые вам нужны.

import {SET_USER_NAME} from '../constants/userConstants'
import * as actions from '../shared/actions/baseActions'

Тогда мы можем передать действие нашему компоненту в атрибуте

setName={setString(SET_USER_NAME)}

Ниже приведен компонент User с примером асинхронного действия.

import React, {Component } from 'react'
import PropTypes from 'prop-types'

export default class User extends Component {
    setAfterTimeout (params = {}) {
        //This method is for demonstration. Better if it is imported from external location
        const {payload, dispatch, getState} = params
        const state = getState() //Retrieve current state if needed
        return new Promise((res) => {
            //Call another dispatch if needed
            dispatch({
                type: 'SET_USER_NAME',
                payload: 'test'
            })

            //Call async method before dispatch
            setTimeout(() => {
                res(`${payload || ''} Doe`)
            }, 2000)
        })
    }
    render() {
        const { name, avatar, setName} = this.props
        return <div>
            <p>Hi, {name}!</p>
            <img onClick={() =>setName('John', this.setAfterTimeout)} src={avatar} alt=""/>
            {/*If you don't need initial payload, set it to null. e.g.  {() =>setName(null, this.setAfterTimeout)}*/}
        </div>
    }
}

User.propTypes = {
    name: PropTypes.string.isRequired,
    avatar: PropTypes.string.isRequired
}

setAfterTimeout - это метод преобразования, позволяющий манипулировать асинхронными данными перед отправкой.

Мой главный вопрос: Каковы потенциальные ловушки использования этого шаблона? Я думаю, что самым большим плюсом является то, что не нужно создавать действия для базового магазина ammendemnts, например Пользовательский интерфейс, который находится в магазине. Это также позволяет преобразовать полезную нагрузку с помощью асинхронного обратного вызова перед отправкой.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...