Недавно начал работать с 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, например Пользовательский интерфейс, который находится в магазине. Это также позволяет преобразовать полезную нагрузку с помощью асинхронного обратного вызова перед отправкой.