react-reasm
v0.1.1
Published
A simplify work flow for redux
Downloads
5
Maintainers
Readme
React Reasm
I think that convention over configuration.
This is a repo of react and redux burket and I want to simplify redux flow for a react production. I think that a react production is following some rules is beneficial prefer to not. So I try to make a method of "avoid unnecessary troubles" by some rules and conventions.
Warning: The repo is in testing status now. I don't recommend using this in publishing production. But I hope to received suggestion for the repo, thanks.
Install
It likes a introduction more than a library. But I accumulate react burket in the repo.
npm install react-reasm
And the repo is contains these libraries.
react-redux
redux
redux-observable
reselect
rxjs
Introduction
I hope the redux flow is following the rule in every time.
- define actions
export const SampleActionType = {
TODO_LIST_CLEAR: 'SAMPLE_ACTION_TODO_LIST_CLEAR',
}
export const SampleRequestType = {
TODO_GET_LIST: 'SAMPLE_REQUEST_TODO_GET_LIST',
TODO_ADD_ITEM: 'SAMPLE_REQUEST_TODO_ADD_ITEM',
}
export const SamplePayloadType = {
TODO_LOADED: 'SAMPLE_REQUEST_TODO_LOADED',
TODO_ERROR: 'SAMPLE_REQUEST_TODO_ERROR',
TODO_LIST: 'SAMPLE_REQUEST_TODO_LIST',
TODO_LIST_ITEM: 'SAMPLE_REQUEST_TODO_LIST_ITEM',
}
We can classify actions into three parts likes "actions", "requests", "payloads" I think that http requests is most commonly in any project, so I distinguish the http request status of "requests" and "payloads"
This mind, May I not using the common actions, will verify in future. I think that it's unnecessary actions for a well design project.
- create actions
export const sampleActions = {
clearTodoList: () => ({
type: SampleActionType.TODO_LIST_CLEAR,
}),
}
export const sampleRequests = {
getTodoList: () => ({
type: SampleRequestType.TODO_GET_LIST,
}),
addTodoListItem: (item) => ({
type: SampleRequestType.TODO_ADD_ITEM,
params: { item },
}),
}
export const samplePayloads = {
setTodoList: (data) => ({
type: SamplePayloadType.TODO_LIST,
payload: data,
}),
setTodoListItem: (data) => ({
type: SamplePayloadType.TODO_LIST_ITEM,
payload: data,
}),
setTodoError: (data) => ({
type: SamplePayloadType.TODO_ERROR,
error: data,
}),
setTodoLoaded: () => ({
type: SamplePayloadType.TODO_LOADED,
}),
}
- configure mapper
export const sampleRequestMapper = {
[SampleRequestType.TODO_GET_LIST]: {
target: mockApi.getTodoList,
payload: samplePayloads.setTodoList,
failure: samplePayloads.setTodoError,
},
[SampleRequestType.TODO_GET_LIST]: {
target: mockApi.addItem,
payload: samplePayloads.setTodoListItem,
failure: samplePayloads.setTodoError,
},
}
The mapper can redirect your request action to target
.
Attaction the target must be a Promise
.
I consider supporting the "async function"
- update reducer
import { createReducer } from 'react-reasm'
const initialState = {
todo: {
list: [],
},
}
const reducerMapper = (state, action) => ({
[SampleActionType.TODO_LIST]: {
...state,
todo: {
...state.todo,
list: initialState.todo.list,
},
},
[SamplePayloadType.TODO_LIST]: {
...state,
todo: {
...state.todo,
list: action.payload,
},
},
[SamplePayloadType.TODO_LIST_ITEM]: {
...state,
todo: {
...state.todo,
list: [
...state.todo.list,
action.payload,
],
},
},
})
export const sampleReducer = createReducer({
initialState,
reducerMapper,
})
This only transform the reducer structs from "switch" to "mapper".
I like "mapper" perfer "switch" becauce "mapper" only provide defined method to operation.
- set selector
const sampleSelector = {
todos: (state) => state.sample.todo,
}
export const selectSampleTodos = (selector) => ({
selectors: [sampleSelector.todos],
combiner: (state) => selector(state),
})
The default selector likes (state) => state.sample.todo
. But I think it's not very well.
You can use the defalut selector for you like.
- configure store
import { configureStore } from 'react-reasm';
export const store = configureStore({
reducers: rootReducer,
mappers: rootMapper,
middlewares: [ tempMiddleware ],
})
In the end, you must configure store and set into Provider
like redux.
- connect component
import { Injector } from 'react-reasm';
const injectStateProps = {
todoList: selectSampleTodos((state) => state.list)
}
const injectActions = {
getTodoList: sampleRequest.getTodoList,
addTodoItem: sampleRequest.addTodoListItem,
}
const TSample = (props) => {
const { todoList, getTodoList, addTodoItem } = props
return (
...
)
}
export const Sample = Injector({
props: injectStateProps,
actions: injectActions,
})(TSample)
The Injector
is combined the connect
for react-redux
.
If it not work, you can see the sample project todolist.
Request Status
Request status is a common props for a most of project. So I provide a method for manange request status
const initialState = {
...,
requests: {
loading: {},
error: {},
success: {},
},
}
export const sampleReducer = createReducer({
...,
requestTypes: SampleRequestType,
payloadTypes: SamplePayloadType,
})
When you set requestTypes
and payloadTypes
in createReducer
, You can select the request status by reducer.
import { combineRequests } from 'react-reasm';
const sampleSelector = {
...,
requests: (state) => state.sample.requests,
}
export const selectSampleRequests = (selector) => ({
selectors: [sampleSelector.requests],
combiner: (state) => selector(combineRequests(state, sampleRequests)),
})
It likes normal selector but the function combineRequests
select request status for you like,
import { Injector } from 'react-reasm';
const injectStateProps = {
...,
status: selectSampleRequests((state) => state.getTodoList)
success: selectSampleRequests((state) => state.addTodoItem.success)
}
const TSample = (props) => {
const { status, success } = props
return (
...
)
}
By the design, I think that the most complex module is selectors. I will solve this question in future.
Inject Components
You can append any components in the injected component. But I recommend to inject "modals" and "redirect" etc.
If you want inject modals, I recommend the other repo for inject modals react-debris
const TModal = (props) => {
const onClose = () => {
props.hooks.hide()
}
return (
<div onClick={onClose}>
Test Content
</div>
)
}
const Modal = Popup.enhancePopupComponent(TModal)
const injectComponents = {
testModal: (inject) => {
const hooks = Popup.usePopupHooks()
React.useEffect(() => inject(hooks), [])
return <Modal hooks={hooks} />
},
}
const TSample = (props) => {
const { testModal } = props
const onClickHandle = () => {
testModal.show()
}
return (
...
)
}
export const Sample = Injector({
... ,
components: injectComponents,
})(TSample)
You can inject Redirect
by react-router-dom
also.
NOT USABLE
const injectComponents = {
redirectToHome: (inject) => {
const [ redirect, setRedirect ] = React.useState(false)
inject(() => setRedirect(true))
return redirect && <Redirect to="/" />
},
}
const TSample = (props) => {
const { redirectToHome } = props
const onClickHandle = () => {
props.redirectToHome()
}
return (
...
)
}
export const Sample = Injector({
... ,
components: injectComponents,
})(TSample)
Examples
These examples was built by create-react-app
and you can run npm start
in the example of todolist
Simple Road Map
- Clean up the code. (Because these code only usable now.)
- Complate typescript. (The reason is the same as the previous.)
- More rules. (I think that convention over configuration.) ...