An open API service indexing awesome lists of open source software.

https://github.com/coolshare/reactreduxpattern

This package as a starter kit targeting design patterns of React/Redux
https://github.com/coolshare/reactreduxpattern

coolshare design patterns react reducer redux redux-store

Last synced: 2 months ago
JSON representation

This package as a starter kit targeting design patterns of React/Redux

Awesome Lists containing this project

README

          

React-Redux Patterns
========================

By Mark Qian 6/2017 (markqian@hotmail.com)

A. The demo page:

http://coolshare.com/ReactReduxPattern

B. Description:

This package is designed to get you up and running as a comprehensive web application.
Another major goal of this package is to illustrate commonly used patterns in a React application.
I will first focus on some of the patterns I introduced for my own usage in my projects at work.
Then I will list some commonly used ones. The patterns introduced and used by me:

- Inheritant of html

Problem: There is no a "nature way" to inherit html from a super class in React.
And FaceBook even recommends giving up inheritance at all:

"At Facebook, we use React in thousands of components, and we haven't found any use cases where we would recommend creating component inheritance hierarchies.
Props and composition give you all the flexibility you need to customize a component's look and behavior in an explicit and safe way. Remember that components may accept arbitrary props, including primitive values, React elements, or functions.
If you want to reuse non-UI functionality between components, we suggest extracting it into a separate JavaScript module. The components may import it and use that function, object, or a class, without extending it."


This conclusion does not make sense at all. One of the major goals of ES6 is to bring javascript to a higher level: object oriented. Object oriented is a needed pattern in many cases.

For example, when building series of gadgets for a portal, each gadget has the same html as header where min/max/close boxes are defined. In order to share that common html, we need a class with the header html to by shared by all the gadget classes (concrete gadgets).

Per the recommendation of the Facebook article above, to use the composition pattern, you need to have a parent class, say Gadget, and a series of child classes like GadgetOne, GadgetTwo, GadgetThree...as the following

```
function Gadget(props) {
renderHeader() {
//...
}
render() {
return (


{this.renderHeader()}
{props.children}

);
}
}

function GadgetOne(props) { return (
...
); }
```

and HTML as the following









What is the problem with this pattern?

1). There are many redundant codes in html

2). The HTML is not readable at all: you see a lot of "Gadget"s but they do not really mean anything

3). Gadget is a parent/abstract class which has nothing to do with concrete content: you never want to expose it. What if you have multiple parents class that your child gadgets share features from and are you going to end up with something like the following to represent a single gadget?


...



...

The problem here is that the relation between Gadget and GadgetOne is not composition but inheritant.



Solution: the inheritant is the key to fix the problem:

The super class:


export default class Gadget extends React.Component{
renderHeader() {
return (

...

)
}
renderMe() {
return null;
}
render() {
return (

{this.renderHeader()}
{this.renderMe()}

)
}

}

And the sub class:

import Gadget from '../../../../common/Gadget'
class GadgetOne extends Gadget{
/**
* renderMe called in super
* @return {ReactElement} markup
*/
renderMe(){

//...
return (


//...

)
}
}

And HTML:




Result:


1). No redundant HTML codes

2). Very readable

3). The super class is not expose in HTML




- Loading components by string class name from a config file

Problem: In some case, you can not import your custom React classes statically since you don't have them yet.
One example is a portal where users contribute their own gadgets after you release the portal application. When a new gadget is created by a user, two things are done:


1). the gadget class is placed in a location that the application can reach.

2). the location of the gadget class is added to the config file, GadgetConfig.js


Now how to load it, place it on a page and even inject some dynamic properties to it?

Solution: here is the step to load and place into a page
1). import the config file GadgetConfig.js


import gadgetConfig from '../config/GadgetConfig'


where GadgetConfig.js contains


export default const gadgetConfig = {
"GadgetOne":{"name":"GadgetOne", "path":"../gadgets/GadgetOne", "state":"normal"},
"GadgetTwo":{"name":"GadgetTwo", "path":"../gadgets/GadgetTwo", "state":"normal"},
...
}
And the GadgetOne.js contains


export default class GadgetOne extends React.Component{
render(){
...
}
}

2). import gadgets dynamically

for (let k in gadgetConfig) {
let g = cm.gadgetConfig[k];
let type = require(g.path).default;
g.elem = React.createElement(type)
}

3). Place the GadgetOne into a page and inject a property


render(){
var self = this;
...
let elems = Object.keys(gadgetConfig).map((k, idx)=>{
let gadget = gadgets[k];
return (React.cloneElement(gadget.elem, {"key":idx, "myProps":self.props.myProps}))
})
return (


{elems}

)
}



- Store Customization

Problem: Access to the store and store related methods from anywhere is not easy and using many store related methods as-is does not meet our need.
For example, we need a dispatch with callback but the as-is dispatch of Redux store
does not provide that. We need a global access point to access store and store related methods.


Solution: Creating a singleton wrapper instance that can be accessed globally. It holds the reference of Redux store and the wrapper of
store related methods that satisfies custom need. Here is how to access store anywhere:


```
import cs from "./services/CommunicationManager";

cs.getStoreValue("MyReducer", "myVar");
cs.dispatch({"type":"myType", ...}, myCallback); //custom callback that will be described below
cs.subscribe("myType", handler);
```

See code details at /services/CommunicationService.js and /index.js.
The CommunicationService will do a lot more that I will describe below.

- Dispatch with callback

Problem: dispatch of Redux store does not allow callback. This is not convenient since you sometimes want to write the handler in the same place
of dispatching instead of somewhere else such as in a reducer.


Solution: one key issue with this is that the callback has to be invoked after every handler including reduces and subscribers is done their jobs.
So my approach is to trigger an asynchronous dispatch in a middleware and the asynchronous dispatching is picked up in the next round of event process in
a common reducer where the callback is invoked. Here is the way to use it:

```
cs.dispatch({"MyType", "data":"mydata"}, action=> {
//handle callback here
})

```
See code details at /services/CommunicationService.js, /components/CommonReducer.js and /components/CommonMiddleware.js.

- Popup Stack

Problem: This is not a React specific issue. In your application, in many case to achieve a better user experience, you need to allow users
to jump into another point in the component hierarchy. If you simply route (deep linking programmatically or allow user to jump by clicking in some case)
to the point you may lost the current stay. The use may totally get lost after they finish the job in current stack level. So you need a state "Stack".


Another issue you face is that if what you popup is an general component, there could be another "short-cut" link/button pointing to another component.
You better not use modal dialog since it may result multi-level modal dialogs, a bad UI behave.


Solution: I built a component/container, "StackViewContainer". It keeps all level of the stack "modal" so that users have to close all the popups to
return when "drilling down" or jumping around. In the running demo, try it out by clicking link "React Patterns" at the top and click at "Popup Pattern" on the
left menu which links to an arbitrary component, "Housing Info". This "Housing Info" is "modal" since it hides everything behind but you do not feel it as
a dialog but a full page at top of the previous page. Next you can popup more by clicking "Trading Info" at the top-right. You can not go nowhere except
clicking at "X" button to return. See code details at /components/StackViewContainer.js. Invocation is easy as

```
cs.popup(MyComponent, "MyComponent");
```

- Wrapper for Redux

Problem: Redux does a simple pub/sub. All the reducers and subscribers will be invoke for any dispatching (This is really not efficient at all. I am wondering
why they don't use type to map the listeners so that not all the listeners are called for each single action dispatching).
So you have to place if statement in all the subscribers to only let the corresponding invocation through. Another issue with Redux built-in subscribe is that
there is access to action handily and LastAction may not available all the time.


Solution: I wrote a wrapper, "subscribe" to hide the filtering within the wrapper and inject the action as a parameter. So you can simply subscribe as :


```
cs.subscribe("myType", action=>{
//handling here
});
```
Unsubscribe is also wrap so that you don't need to save the function reference return by subscribe. Just:
```
cs.unsubscribe("myType");
```

See code details at /services/CommunicationService.js;

Actually, cs.dispatch is another example of wrapper.

- Dispatch as pub/sub

Problem: In some case, you want to handle a dispatching in a variety of places/components instead of reducers, or you simply need a pub/sub communication that has nothing to do
with saving anything in the store: you only want to let other parties know something happen (with data). But Redux's dispatch only deal with reducers and Redux never upates variables like
LastAction when Redux can not find a reducer. So you can not even identify that a dispatching is sent to whom in a subscriber (listener) since the action is never saved anywhere.
of action.


Solution: I add a middleware to collect the action before all the listeners are invoked and then use the collected action in the listeners. In this way, you can handle a dispatched action anywhere out side reducer.

- Dispatch and subscribe in HTML

Problem: Don't you feel so annoy about that you always need to write handlers to deal with users activity. In many cases, you really just like to trigger a dispatch with simple data
or without data: you simply don't want to write a handler!


Solution: I introduced two simple components to do communication in HTML:
- Dispatcher

Dispatcher allows you to dispatch an action when wrapped element(its child element) is interacted by user like clicking:



An input dispatch "test1" on "onChange":

```

```


where the value of input will be delivered as the ation.data. Additionally, if you want to set a specific field in the Redux state, you can do it as the following



An input dispatch "test1" on "onChange" to set to field "test1" for action type "pubsubTest":






- Subscriber

Subscriber allows you to subscribe an action "type" to receive an action and set action.data as value of its child element (the input):



A Input subscribes actionType "test2":
where ation.data will be set as the value of input. You can also specify what specific field you like to set to the subscribed element (the input).

To try this demo yourself, just select "React Patterns" at the top link. Than, select "Pubsub Pattern" at the left and have fun!
See code details at /common/Dispatcher.js, /common/Subscriber.js, /components/Patterns/RightPane/Pubsub/PubsubComponent.js


- Pub/sub Pattern

Problem: the major communication in Redux is that one party dispatches an action and listeners (reducers) receive the action and process it to impact views.
But in a complicate application, you sometimes need more handy ways to communicate with other parties. For example, you want to simple publish (dispatch in term of Redux) a topic
(action in term of Redux) to subscribers (reducers in term of Redux) in JSX-html instead of defining a handler to dispatch for an UI activity. Or like I discuss in the previous
item above, "you only want to let other parties know something happen (with data)" and there is no synchronized impact to the views yet, meaning that the result of the dispatching
may not need reducers.


Solution: I introduced a toolkit, CoolshareReactPubSub.


You can publish in html like


```
render() {
return (


Left
aaaa


A
B






);
}
```


And subscribe in html like

```


```


/**** here are some commonly used ones***/

- React patterns. the following patterns are used in the application

1). Container/Component. It is used under /components/Patterns: all the components under this directory are written with this pattern.


See details in /components/Patterns/RightPane/Photo/PhotoComponent.js and /components/Patterns/RightPane/Photo/PhotoContainer.js



2). State hoisting and Stateless function (pure function): Events are changes in state. Their data needs to be passed to stateful container components parents. Example (in VideoContainer.js and VideoComponent.js):

The event handler resides in VideoContainer and VideoComponent hoists the data entered by users to
VideoContainer:

class _VideoContainer extends React.Component {
handlePeriod(s) {
...
}
render() {
...
return (
< VideoComponent handlePeriod={this.handlePeriod.bind(this)}}... />
...
}
}
export default class VideoComponent extends React.Component{
render() {
...
return (
this.props.handlePeriod(e.target.value)}>
...

}
}
}
and VideoComponent is a stateless "function".

See details in /components/Patterns/RightPane/Video/VideoComponent.js and /components/Patterns/RightPane/Video/VideoContainer.js


3). conditional rendering. The is an alternative of routing to show different content/page. Example (in MapContainer.js):

class _MapContainer extends Component {
...
render() {
return (
...
{(this.props.currentMap==="GoogleMap") &&

Some bus stops in SF
}
{(this.props.currentMap==="MapBox") && } ...
)
}
}

const MapContainer = connect(
store => {
return {
currentMap: store.MapContainerReducer.currentMap
};
}
)(_MapContainer);
export default MapContainer


See details in /components/MainPage/Maps/MapContainer.js


4).Render Callbacks: a function passed as a prop to a component, which allows that component to render something

A common use-case of a render callback was to allow a child to render something using data it did not receive in props.
Example (RightPane.js)

function ChildPane(children) {
return children(id)
}
class _RightPane extends React.Component{
render(){
let ChildPane = ({ children }) => children (this.props.currentPage)
return (


{id=>id==="TodoList"?:id==="HousingInfo"?:null}


)
}
}

The goal of this _RightPane is to display or according this.props.currentPage passed by the parent container (). We first declare ChildPane as a "function" which access another function (children) as parameter and then invoke the function(children passed as parameter) inside ChildPane. ChildPane is used in the render content where Children receives its function parameter (children) as
{id=>id==="TodoList"?:id==="HousingInfo"?:null}
Then this function is invoke as

children (this.props.currentPage)

where id above is this.props.currentPage. What is good on this pattern? The benefit is that ChildPane can be used somewhere else with different content instead of "{id=>id==="TodoList"?:id==="HousingInfo"?:null}" with the "this.props.currentPag" built-in like a closure.

See details in /components/MainPage/FormTable/RightPane.js


5).Proxy Component: Wrapping a component with attributes and reuse it.

Example (in TodoList.js)

const Td = props =>

class _TodoList extends React.Component{
...
render(){
...
return (

{todo.id}
{todo.text}
See details in /components/MainPage/FormTable/TodoList/TodoList.js

The third party software used in the package:


- Basic function/feature of Redux: connect of React-redux, middleware, dispatching actions, subscription and so on.
This kit uses a pure Redux pattern in the area communication and view update so no "setState" is used except local
state like input content state impact button enable state.

- Other the 3nd-party lib are used included:

mapbox-gl, googlemap, react-data-grid, infinite-tree, react-image-gallery, react-tabs, react-youtube


C. Instructions for installation

1. download the zip file of this package and unzip it to, say c:\ReactReduxPattern

or simply run the following


cd c:\
git clone https://github.com/coolshare/ReactReduxPattern.git ReactReduxPattern


2. install environment

cd c:\ReactReduxPattern

npm install

3. run the application

npm start

4. build a production version

webpack -p



Go Mark's home page http://MarkQian.com to see more.

So my approach is to trigger an asynchronous dispatch in a middleware and the asynchronous dispatching is picked up in the next round of event process in
a common reducer where the callback is invoked. Here is the way to use it:

```
cs.dispatch({"MyType", "data":"mydata"}, action=> {
//handle callback here
})

```
See code details at /services/CommunicationService.js, /components/CommonReducer.js and /components/CommonMiddleware.js.

- Popup Stack

Problem: This is not a React specific issue. In your application, in many case to achieve a better user experience, you need to allow users
to jump into another point in the component hierarchy. If you simply route (deep linking programmatically or allow user to jump by clicking in some case)
to the point you may lost the current stay. The use may totally get lost after they finish the job in current stack level. So you need a state "Stack".


Another issue you face is that if what you popup is an general component, there could be another "short-cut" link/button pointing to another component.
You better not use modal dialog since it may result multi-level modal dialogs, a bad UI behave.


Solution: I built a component/container, "StackViewContainer". It keeps all level of the stack "modal" so that users have to close all the popups to
return when "drilling down" or jumping around. In the running demo, try it out by clicking link "React Patterns" at the top and click at "Popup Pattern" on the
left menu which links to an arbitrary component, "Housing Info". This "Housing Info" is "modal" since it hides everything behind but you do not feel it as
a dialog but a full page at top of the previous page. Next you can popup more by clicking "Trading Info" at the top-right. You can not go nowhere except
clicking at "X" button to return. See code details at /components/StackViewContainer.js. Invocation is easy as

```
cs.popup(MyComponent, "MyComponent");
```

- Wrapper for Redux

Problem: Redux does a simple pub/sub. All the reducers and subscribers will be invoke for any dispatching (This is really not efficient at all. I am wondering
why they don't use type to map the listeners so that not all the listeners are called for each single action dispatching).
So you have to place if statement in all the subscribers to only let the corresponding invocation through. Another issue with Redux built-in subscribe is that
there is access to action handily and LastAction may not available all the time.


Solution: I wrote a wrapper, "subscribe" to hide the filtering within the wrapper and inject the action as a parameter. So you can simply subscribe as :


```
cs.subscribe("myType", action=>{
//handling here
});
```
Unsubscribe is also wrap so that you don't need to save the function reference return by subscribe. Just:
```
cs.unsubscribe("myType");
```

See code details at /services/CommunicationService.js;

Actually, cs.dispatch is another example of wrapper.

- Dispatch as pub/sub

Problem: In some case, you want to handle a dispatching in a variety of places/components instead of reducers, or you simply need a pub/sub communication that has nothing to do
with saving anything in the store: you only want to let other parties know something happen (with data). But Redux's dispatch only deal with reducers and Redux never upates variables like
LastAction when Redux can not find a reducer. So you can not even identify that a dispatching is sent to whom in a subscriber (listener) since the action is never saved anywhere.
of action.


Solution: I add a middleware to collect the action before all the listeners are invoked and then use the collected action in the listeners. In this way, you can handle a dispatched action anywhere out side reducer.

- Dispatch and subscribe in HTML

Problem: Don't you feel so annoy about that you always need to write handlers to deal with users activity. In many cases, you really just like to trigger a dispatch with simple data
or without data: you simply don't want to write a handler!


Solution: I introduced two simple components to do communication in HTML:
- Dispatcher

Dispatcher allows you to dispatch an action when wrapped element(its child element) is interacted by user like clicking:



An input dispatch "test1" on "onChange":

```

```


where the value of input will be delivered as the ation.data. Additionally, if you want to set a specific field in the Redux state, you can do it as the following



An input dispatch "test1" on "onChange" to set to field "test1" for action type "pubsubTest":






- Subscriber

Subscriber allows you to subscribe an action "type" to receive an action and set action.data as value of its child element (the input):



A Input subscribes actionType "test2":
where ation.data will be set as the value of input. You can also specify what specific field you like to set to the subscribed element (the input).

To try this demo yourself, just select "React Patterns" at the top link. Than, select "Pubsub Pattern" at the left and have fun!
See code details at /common/Dispatcher.js, /common/Subscriber.js, /components/Patterns/RightPane/Pubsub/PubsubComponent.js


- Pub/sub Pattern

Problem: the major communication in Redux is that one party dispatches an action and listeners (reducers) receive the action and process it to impact views.
But in a complicate application, you sometimes need more handy ways to communicate with other parties. For example, you want to simple publish (dispatch in term of Redux) a topic
(action in term of Redux) to subscribers (reducers in term of Redux) in JSX-html instead of defining a handler to dispatch for an UI activity. Or like I discuss in the previous
item above, "you only want to let other parties know something happen (with data)" and there is no synchronized impact to the views yet, meaning that the result of the dispatching
may not need reducers.


Solution: I introduced a toolkit, CoolshareReactPubSub.


You can publish in html like


```
render() {
return (


Left
aaaa


A
B






);
}
```


And subscribe in html like

```


```


/**** here are some commonly used ones***/

- React patterns. the following patterns are used in the application

1). Container/Component. It is used under /components/Patterns: all the components under this directory are written with this pattern.


See details in /components/Patterns/RightPane/Photo/PhotoComponent.js and /components/Patterns/RightPane/Photo/PhotoContainer.js



2). State hoisting and Stateless function (pure function): Events are changes in state. Their data needs to be passed to stateful container components parents. Example (in VideoContainer.js and VideoComponent.js):

The event handler resides in VideoContainer and VideoComponent hoists the data entered by users to
VideoContainer:

class _VideoContainer extends React.Component {
handlePeriod(s) {
...
}
render() {
...
return (
< VideoComponent handlePeriod={this.handlePeriod.bind(this)}}... />
...
}
}
export default class VideoComponent extends React.Component{
render() {
...
return (
this.props.handlePeriod(e.target.value)}>
...

}
}
}
and VideoComponent is a stateless "function".

See details in /components/Patterns/RightPane/Video/VideoComponent.js and /components/Patterns/RightPane/Video/VideoContainer.js


3). conditional rendering. The is an alternative of routing to show different content/page. Example (in MapContainer.js):

class _MapContainer extends Component {
...
render() {
return (
...
{(this.props.currentMap==="GoogleMap") &&

Some bus stops in SF
}
{(this.props.currentMap==="MapBox") && } ...
)
}
}

const MapContainer = connect(
store => {
return {
currentMap: store.MapContainerReducer.currentMap
};
}
)(_MapContainer);
export default MapContainer


See details in /components/MainPage/Maps/MapContainer.js


4).Render Callbacks: a function passed as a prop to a component, which allows that component to render something

A common use-case of a render callback was to allow a child to render something using data it did not receive in props.
Example (RightPane.js)

function ChildPane(children) {
return children(id)
}
class _RightPane extends React.Component{
render(){
let ChildPane = ({ children }) => children (this.props.currentPage)
return (


{id=>id==="TodoList"?:id==="HousingInfo"?:null}


)
}
}

The goal of this _RightPane is to display or according this.props.currentPage passed by the parent container (). We first declare ChildPane as a "function" which access another function (children) as parameter and then invoke the function(children passed as parameter) inside ChildPane. ChildPane is used in the render content where Children receives its function parameter (children) as
{id=>id==="TodoList"?:id==="HousingInfo"?:null}
Then this function is invoke as

children (this.props.currentPage)

where id above is this.props.currentPage. What is good on this pattern? The benefit is that ChildPane can be used somewhere else with different content instead of "{id=>id==="TodoList"?:id==="HousingInfo"?:null}" with the "this.props.currentPag" built-in like a closure.

See details in /components/MainPage/FormTable/RightPane.js


5).Proxy Component: Wrapping a component with attributes and reuse it.

Example (in TodoList.js)

const Td = props =>

class _TodoList extends React.Component{
...
render(){
...
return (

{todo.id}
{todo.text}
See details in /components/MainPage/FormTable/TodoList/TodoList.js

The third party software used in the package:


- Basic function/feature of Redux: connect of React-redux, middleware, dispatching actions, subscription and so on.
This kit uses a pure Redux pattern in the area communication and view update so no "setState" is used except local
state like input content state impact button enable state.

- Other the 3nd-party lib are used included:

mapbox-gl, googlemap, react-data-grid, infinite-tree, react-image-gallery, react-tabs, react-youtube


C. Instructions for installation

1. download the zip file of this package and unzip it to, say c:\ReactReduxPattern

or simply run the following


cd c:\
git clone https://github.com/coolshare/ReactReduxPattern.git ReactReduxPattern


2. install environment

cd c:\ReactReduxPattern

npm install

3. run the application

npm start

4. build a production version

webpack -p



Go Mark's home page http://MarkQian.com to see more.