rwr-redux 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.eslintrc +8 -0
- data/.rubocop.yml +4 -0
- data/CHANGELOG.md +16 -1
- data/README.md +80 -13
- data/Rakefile +21 -2
- data/docs/rails-redux-router.md +108 -0
- data/js/src/index.js +11 -98
- data/js/src/integrations/redux-container.js +69 -0
- data/js/src/integrations/redux-router.js +96 -0
- data/js/src/integrations/redux-store.js +50 -0
- data/js/src/version.js +1 -1
- data/js/test/helpers/redux-router.js +34 -0
- data/js/test/index.spec.js +19 -0
- data/js/test/integrations/redux-container.spec.js +163 -0
- data/js/test/integrations/redux-router.spec.js +147 -0
- data/js/test/integrations/redux-store.spec.js +105 -0
- data/lib/react_webpack_rails/redux_integration/services/redux_container.rb +27 -0
- data/lib/react_webpack_rails/redux_integration/services/redux_element.rb +33 -0
- data/lib/react_webpack_rails/redux_integration/services/redux_router.rb +27 -0
- data/lib/react_webpack_rails/redux_integration/version.rb +1 -1
- data/lib/react_webpack_rails/redux_integration/view_helpers.rb +39 -3
- data/package.json +21 -16
- data/redux_integration.gemspec +3 -2
- metadata +32 -5
- data/js/test/index.js +0 -188
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d90aef2f8a8cd36bd034731da93afa59c6645f55
|
4
|
+
data.tar.gz: c41a41d679bf180cfd8c6e490c1b27bb393c9ebb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 15a686a3d097a50e7fd95f37784ee56be0e280f357c01d0dd2d373140de895a50f20357a7c1a1d00f7b988d57b40d6a76d3edc48edab1bfa831138862373a1e4
|
7
|
+
data.tar.gz: 1c6a123ac3b564d88e5fd8c146ea56a54a59d095a409a2094b1679dd01ed9dcb4818c4f10fa6670a5f9079cc5f196c5ad250a0ebdc8eb389eb69f5d4c223081e
|
data/.eslintrc
ADDED
data/.rubocop.yml
ADDED
data/CHANGELOG.md
CHANGED
@@ -1 +1,16 @@
|
|
1
|
-
##
|
1
|
+
## 0.2.0
|
2
|
+
* add server side rendering
|
3
|
+
* add redux-router helpers:
|
4
|
+
* js: `registerRouter`
|
5
|
+
* Rails: `redux_router`
|
6
|
+
|
7
|
+
## 0.1.1 initial release
|
8
|
+
|
9
|
+
* JS helpers:
|
10
|
+
* `registerStore`
|
11
|
+
* `registerContainer`
|
12
|
+
|
13
|
+
|
14
|
+
* Rails helpers:
|
15
|
+
* `redux_store`
|
16
|
+
* `redux_container`
|
data/README.md
CHANGED
@@ -2,24 +2,39 @@ rwr-redux
|
|
2
2
|
====
|
3
3
|
[Redux.js](http://redux.js.org/) integration plugin for [react_webpack_rails](https://github.com/netguru/react_webpack_rails).
|
4
4
|
|
5
|
-
|
5
|
+
It allows you to use Redux state containers in a different part of Rails views. Thanks to this gem you can use multiple components (Redux containers) on one page. They can easily access the same store and have their state synced.
|
6
|
+
|
7
|
+
#### Guides and Examples
|
8
|
+
* basic react redux rails example: [app](https://github.com/caspg/rails-react-examples/tree/master/basic-redux)
|
9
|
+
* how to use Redux and react-router in Rails app: [guide](https://github.com/netguru/rwr-redux/blob/master/docs/rails-redux-router.md) and [example](https://github.com/caspg/rails-react-examples/tree/master/redux-router).
|
10
|
+
|
6
11
|
|
7
12
|
## Setup
|
8
13
|
* Add `rwr-redux` to your Gemfile:
|
9
14
|
|
10
|
-
```
|
15
|
+
```ruby
|
11
16
|
gem 'rwr-redux'
|
12
17
|
```
|
13
18
|
|
14
|
-
* Install rwr-redux
|
19
|
+
* Install `rwr-redux` and `redux` packages:
|
15
20
|
|
16
21
|
```
|
17
|
-
$ npm install --save rwr-redux
|
22
|
+
$ npm install --save redux react-redux rwr-redux
|
18
23
|
```
|
19
24
|
|
20
25
|
## Usage
|
21
26
|
|
22
|
-
|
27
|
+
First of all, you have to register your store and containers in `react/index.js`. Then you can use them in a Rails view using provided helpers.
|
28
|
+
When a page is loaded, your container component is wrapped with [`<Provider>`](https://github.com/reactjs/react-redux/blob/master/docs/api.md#provider-store) component and will have access to defined store.
|
29
|
+
|
30
|
+
|
31
|
+
### register integrations, store and components in `react/index.js`
|
32
|
+
|
33
|
+
Register integrations:
|
34
|
+
```js
|
35
|
+
integrationsManager.register('redux-store', RWRRedux.storeIntegrationWrapper);
|
36
|
+
integrationsManager.register('redux-container', RWRRedux.containerIntegrationWrapper);
|
37
|
+
```
|
23
38
|
|
24
39
|
Register store:
|
25
40
|
|
@@ -35,7 +50,17 @@ import Container from './containers/MyContainerName';
|
|
35
50
|
RWRRedux.registerContainer('MyContainerName', Container);
|
36
51
|
```
|
37
52
|
|
38
|
-
###
|
53
|
+
### store
|
54
|
+
|
55
|
+
Registered store has to be a function which accepts **initial state** as an argument and returns store object:
|
56
|
+
|
57
|
+
```js
|
58
|
+
export default function configureStore(initialState) {
|
59
|
+
return createStore(rootReducer, initialState);
|
60
|
+
}
|
61
|
+
```
|
62
|
+
|
63
|
+
### use registered store and components in Rails view
|
39
64
|
|
40
65
|
Define store with initial state:
|
41
66
|
|
@@ -55,23 +80,65 @@ If you have more than one store in a view, you can specify `store_name`:
|
|
55
80
|
<%= redux_container 'MyContainerName', store_name: 'MyStoreName' %>
|
56
81
|
```
|
57
82
|
|
58
|
-
|
83
|
+
## Usage with react-redux-router
|
59
84
|
|
60
|
-
|
85
|
+
If you want to use router in your redux app, you have to only create routes component. `rwr-redux` will wrap it with `<Router>` and `<Provider>`components, and also, will sync history with the store. Only `browserHistory` is supported.
|
61
86
|
|
62
|
-
|
87
|
+
### example routes
|
88
|
+
`app/react/routes/index.js`
|
63
89
|
|
64
90
|
```js
|
65
|
-
|
66
|
-
|
91
|
+
export default (
|
92
|
+
<Route path="/" component={App}>
|
93
|
+
<Route path="about" component={About} />
|
94
|
+
</Route>
|
95
|
+
)
|
67
96
|
```
|
68
97
|
|
69
|
-
|
98
|
+
### register integration and routes in `react/index.js`
|
99
|
+
|
100
|
+
```js
|
101
|
+
integrationsManager.register('redux-router', RWRRedux.routerIntegrationWrapper);
|
102
|
+
```
|
103
|
+
|
104
|
+
```js
|
105
|
+
import RoutesName from './routes';
|
106
|
+
RWRRedux.registerRoutes('RoutesName', RoutesName);
|
107
|
+
```
|
108
|
+
|
109
|
+
### use registered routes in Rails view
|
110
|
+
|
111
|
+
Do not forget to use `redux_store` helper.
|
70
112
|
|
71
113
|
```erb
|
72
|
-
<%=
|
114
|
+
<%= redux_store 'MyStoreName', { foo: @bar } %>
|
115
|
+
|
116
|
+
<%= redux_router 'RoutesName' %>
|
73
117
|
```
|
74
118
|
|
119
|
+
## Server Side Rendering
|
120
|
+
|
121
|
+
More info how to use server side rendering with `react_webpack_rails`: [click](https://github.com/netguru/react_webpack_rails/blob/master/docs/server_side_rendering.md)
|
122
|
+
|
123
|
+
Rails routes has to be properly setup:
|
124
|
+
|
125
|
+
```rb
|
126
|
+
get '/server_side/' => 'pages#server_side'
|
127
|
+
get '/server_side/*path' => 'pages#server_side'
|
128
|
+
```
|
129
|
+
|
130
|
+
To enable server side rendering pass `server_side: true` to helpers options:
|
131
|
+
|
132
|
+
```erb
|
133
|
+
<%= redux_store 'MyStoreName', { foo: @bar }, server_side: true %>
|
134
|
+
|
135
|
+
<%= redux_container 'MyContainerName', server_side: true %>
|
136
|
+
|
137
|
+
<%= redux_router 'RoutesName', server_side: true %>
|
138
|
+
```
|
139
|
+
|
140
|
+
**NOTE**: `rwr-redux` automatically handles matching, redirecting and routing errors. Redirects and 404's are passed to Rails and handled there so you will be redirected or get 404 page like in normal Rails app.
|
141
|
+
|
75
142
|
## Contributing
|
76
143
|
## Issues
|
77
144
|
|
data/Rakefile
CHANGED
@@ -3,7 +3,7 @@ require 'rspec/core/rake_task'
|
|
3
3
|
|
4
4
|
namespace :test do
|
5
5
|
desc 'Run all tests'
|
6
|
-
task all: [:node, :gem] do
|
6
|
+
task all: [:node, :gem, :rails4] do
|
7
7
|
puts 'Finished all tests, yay!'
|
8
8
|
end
|
9
9
|
|
@@ -16,13 +16,27 @@ namespace :test do
|
|
16
16
|
task :gem do
|
17
17
|
sh %Q(bundle exec rspec spec/rwr-redux.rb)
|
18
18
|
end
|
19
|
+
|
20
|
+
desc 'Run rspec for rails4 application'
|
21
|
+
task :rails4 do
|
22
|
+
Bundler.with_clean_env do
|
23
|
+
sh %Q(
|
24
|
+
cd spec/rails4_dummy_app &&
|
25
|
+
npm run build &&
|
26
|
+
npm run rwr-background-node &&
|
27
|
+
sleep 4 &&
|
28
|
+
bundle exec rspec &&
|
29
|
+
npm run rwr-stop-background-node
|
30
|
+
)
|
31
|
+
end
|
32
|
+
end
|
19
33
|
end
|
20
34
|
|
21
35
|
task default: 'test:all'
|
22
36
|
|
23
37
|
namespace :setup do
|
24
38
|
desc 'Prepare every environment'
|
25
|
-
task all: [:node, :gem] do
|
39
|
+
task all: [:node, :gem, :rails4] do
|
26
40
|
puts 'Prepared all, yay!'
|
27
41
|
end
|
28
42
|
|
@@ -35,4 +49,9 @@ namespace :setup do
|
|
35
49
|
task :gem do
|
36
50
|
sh %Q(bundle install)
|
37
51
|
end
|
52
|
+
|
53
|
+
desc 'Prepare rails4 test app dependencies'
|
54
|
+
task :rails4 do
|
55
|
+
sh %Q(npm install && cd spec/rails4_dummy_app && npm install && bundle install)
|
56
|
+
end
|
38
57
|
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# react-router-redux in Rails app
|
2
|
+
|
3
|
+
Adding multiple Redux components within Rails view is really easy thanks to [react_webpack_rails](https://github.com/netguru/react_webpack_rails) and [rwr-redux](https://github.com/netguru/rwr-redux).
|
4
|
+
|
5
|
+
This guide shows how to do routing with `react-router` and how to sync state between components with Redux store using `react-router-redux`.
|
6
|
+
Let's say you would like to have two components which shares the same store, navbar and main app component.
|
7
|
+
|
8
|
+
If you want check the source code right away, you can find example app [here](https://github.com/caspg/rails-react-examples/tree/master/redux-router).
|
9
|
+
|
10
|
+
## Setup
|
11
|
+
|
12
|
+
* [react_webpack_rails](https://github.com/netguru/react_webpack_rails) and [rwr-redux](https://github.com/netguru/rwr-redux) have to be added and to your app
|
13
|
+
* install react-router and react-router-redux:
|
14
|
+
```bash
|
15
|
+
$ npm install --save react-router react-router-redux
|
16
|
+
```
|
17
|
+
|
18
|
+
## Main app component
|
19
|
+
|
20
|
+
In your root component add `<Router>` component and sync history with store.
|
21
|
+
|
22
|
+
`react/containers/Root.jsx` [example](https://github.com/caspg/rails-react-examples/blob/master/redux-router/app/react/containers/Root.jsx)
|
23
|
+
```jsx
|
24
|
+
import React, { Component } from 'react';
|
25
|
+
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
|
26
|
+
import { syncHistoryWithStore } from 'react-router-redux';
|
27
|
+
import RWRRedux from 'rwr-redux';
|
28
|
+
|
29
|
+
import App from './App';
|
30
|
+
(...)
|
31
|
+
|
32
|
+
export default class Root extends Component {
|
33
|
+
componentWillMount() {
|
34
|
+
const mountedStore = RWRRedux.getStore();
|
35
|
+
this.history = syncHistoryWithStore(browserHistory, mountedStore);
|
36
|
+
}
|
37
|
+
|
38
|
+
render() {
|
39
|
+
return (
|
40
|
+
<Router history={this.history} >
|
41
|
+
<Route path="/" component={App}>
|
42
|
+
(...)
|
43
|
+
</Route>
|
44
|
+
</Router>
|
45
|
+
);
|
46
|
+
}
|
47
|
+
}
|
48
|
+
```
|
49
|
+
|
50
|
+
In `componentWillMount()` method, browserHistory is synced with [registered store](https://github.com/netguru/rwr-redux#register-store-and-components-in-reactindexjs). Store can be accessed by `getStore()` function provided by `rwr-redux` package.
|
51
|
+
|
52
|
+
Within this component you can navigate with `<Link>` components ([example](https://github.com/caspg/rails-react-examples/blob/master/redux-router/app/react/components/TabItem.jsx#L11)).
|
53
|
+
|
54
|
+
## Store
|
55
|
+
|
56
|
+
Navbar component will issue [navigation events](https://github.com/reactjs/react-router-redux#what-if-i-want-to-issue-navigation-events-via-redux-actions) via Redux actions. Those events will change state and cause main component to rerender.
|
57
|
+
|
58
|
+
To make it works, you need to apply middleware to the store:
|
59
|
+
`react/store/index.js`
|
60
|
+
```jsx
|
61
|
+
import { createStore, applyMiddleware } from 'redux';
|
62
|
+
import { routerMiddleware } from 'react-router-redux'
|
63
|
+
import { browserHistory } from 'react-router';
|
64
|
+
|
65
|
+
import rootReducer from '../reducers';
|
66
|
+
|
67
|
+
export default function configureStore(initialState) {
|
68
|
+
const middleware = routerMiddleware(browserHistory)
|
69
|
+
|
70
|
+
return createStore(
|
71
|
+
rootReducer,
|
72
|
+
initialState,
|
73
|
+
applyMiddleware(middleware)
|
74
|
+
);
|
75
|
+
}
|
76
|
+
```
|
77
|
+
|
78
|
+
## Navbar component
|
79
|
+
|
80
|
+
`react/containers/Navbar.jsx` [example](https://github.com/caspg/rails-react-examples/blob/master/redux-router/app/react/containers/Navbar.jsx#L15)
|
81
|
+
|
82
|
+
Import `push` method from react-router-redux:
|
83
|
+
```jsx
|
84
|
+
import { push } from 'react-router-redux';
|
85
|
+
```
|
86
|
+
|
87
|
+
And now you can dispatch an action with navigation event:
|
88
|
+
```jsx
|
89
|
+
this.props.dispatch(push(path));
|
90
|
+
```
|
91
|
+
|
92
|
+
## Rails part
|
93
|
+
|
94
|
+
Add wildcard route:
|
95
|
+
|
96
|
+
`routes.rb`
|
97
|
+
```ruby
|
98
|
+
root 'home#index'
|
99
|
+
get '/*path' => 'home#index'
|
100
|
+
```
|
101
|
+
|
102
|
+
Now you can use rails helpers in appropriate view:
|
103
|
+
```erb
|
104
|
+
<%= redux_store 'StoreName', { initial_state: @initial_state_from_controller } %>
|
105
|
+
<%= redux_container 'Navbar' %>
|
106
|
+
|
107
|
+
<%= redux_container 'MainApp' %>
|
108
|
+
```
|
data/js/src/index.js
CHANGED
@@ -1,109 +1,22 @@
|
|
1
1
|
import version from './version';
|
2
|
-
|
3
|
-
import
|
4
|
-
import
|
5
|
-
import { renderToString } from 'react-dom/server';
|
6
|
-
import { Provider } from 'react-redux'
|
7
|
-
|
8
|
-
import { isFunction, isReduxStore } from './utils/validators';
|
2
|
+
import ReduxStore from './integrations/redux-store';
|
3
|
+
import ReduxContainer from './integrations/redux-container';
|
4
|
+
import ReduxRouter from './integrations/redux-router';
|
9
5
|
|
10
6
|
class RWRRedux {
|
11
7
|
constructor() {
|
12
8
|
this.version = version;
|
13
|
-
this.registeredStores = {};
|
14
|
-
this.mountedStores = {};
|
15
|
-
this.defaultStore = null;
|
16
|
-
this.containers = {};
|
17
|
-
|
18
|
-
this.registerStore = this.registerStore.bind(this);
|
19
|
-
this.mountStore = this.mountStore.bind(this);
|
20
|
-
this.getStore = this.getStore.bind(this);
|
21
|
-
this.registerContainer = this.registerContainer.bind(this);
|
22
|
-
this.getContainer = this.getContainer.bind(this);
|
23
|
-
this.createContainer = this.createContainer.bind(this);
|
24
|
-
this.createRootComponent = this.createRootComponent.bind(this);
|
25
|
-
this.renderContainer = this.renderContainer.bind(this);
|
26
|
-
this.unmountContainer = this.unmountContainer.bind(this);
|
27
|
-
this.renderContainerToString = this.renderContainerToString.bind(this);
|
28
|
-
}
|
29
|
-
|
30
|
-
registerStore(name, store) {
|
31
|
-
isFunction(store, `Error when registering '${name}' store: must be a function.`);
|
32
|
-
this.registeredStores[name] = store;
|
33
|
-
}
|
34
|
-
|
35
|
-
mountStore(name, props) {
|
36
|
-
const store = this.registeredStores[name];
|
37
|
-
isFunction(store, `Error when mounting '${name}' store: must be a function.`);
|
38
|
-
|
39
|
-
const storeObject = store(props);
|
40
|
-
isReduxStore(storeObject, `Error when mounting '${name}' store: must be a valid Redux store.`);
|
41
|
-
this.mountedStores[name] = storeObject;
|
42
|
-
this.defaultStore = storeObject;
|
43
|
-
}
|
44
|
-
|
45
|
-
getStore(name) {
|
46
|
-
if (name) {
|
47
|
-
return this.mountedStores[name];
|
48
|
-
} else {
|
49
|
-
return this.defaultStore;
|
50
|
-
}
|
51
|
-
}
|
52
|
-
|
53
|
-
registerContainer(name, container) {
|
54
|
-
this.containers[name] = container;
|
55
|
-
}
|
56
|
-
|
57
|
-
getContainer(name) {
|
58
|
-
return this.containers[name];
|
59
|
-
}
|
60
|
-
|
61
|
-
createContainer(name) {
|
62
|
-
const constructor = this.getContainer(name);
|
63
|
-
return React.createElement(constructor);
|
64
|
-
}
|
65
|
-
|
66
|
-
createRootComponent(name, storeName) {
|
67
|
-
const container = this.createContainer(name);
|
68
|
-
return React.createElement(Provider, { store: this.getStore(storeName) }, container);
|
69
|
-
}
|
70
|
-
|
71
|
-
renderContainer(name, node, storeName) {
|
72
|
-
const rootComponent = this.createRootComponent(name, storeName);
|
73
|
-
render(rootComponent, node);
|
74
|
-
}
|
75
|
-
|
76
|
-
unmountContainer(node) {
|
77
|
-
unmountComponentAtNode(node);
|
78
|
-
}
|
79
|
-
|
80
|
-
renderContainerToString(name, storeName) {
|
81
|
-
const rootComponent = this.createRootComponent(name, storeName);
|
82
|
-
renderToString(rootComponent);
|
83
|
-
}
|
84
|
-
|
85
|
-
get storeIntegrationWrapper() {
|
86
|
-
return {
|
87
|
-
mount: function _mount(_, payload) {
|
88
|
-
this.mountStore(payload.name, payload.props);
|
89
|
-
}.bind(this)
|
90
|
-
}
|
91
|
-
}
|
92
9
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
}.bind(this),
|
10
|
+
this.registerStore = ReduxStore.registerStore;
|
11
|
+
this.mountStore = ReduxStore.mountStore;
|
12
|
+
this.getStore = ReduxStore.getStore;
|
13
|
+
this.storeIntegrationWrapper = ReduxStore.integrationWrapper;
|
98
14
|
|
99
|
-
|
100
|
-
|
101
|
-
}.bind(this),
|
15
|
+
this.registerContainer = ReduxContainer.registerContainer;
|
16
|
+
this.containerIntegrationWrapper = ReduxContainer.integrationWrapper;
|
102
17
|
|
103
|
-
|
104
|
-
|
105
|
-
}.bind(this)
|
106
|
-
}
|
18
|
+
this.registerRoutes = ReduxRouter.registerRoutes;
|
19
|
+
this.routerIntegrationWrapper = ReduxRouter.integrationWrapper;
|
107
20
|
}
|
108
21
|
}
|
109
22
|
|