介绍

仓库地址: https://github.com/sanonz/redux-store-provider
演示地址: https://sanonz.github.io/redux-store-provider/examples/

安装依赖

1
npm install react react-dom react-redux lodash redux-store-provider blueimp-md5 --save

创建 index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Redux Store Provider Example</title>
<link rel="stylesheet" href="https://unpkg.com/bootstrap@4.1.3/dist/css/bootstrap.min.css">
<style>
.content{margin-top:20px;}
</style>
</head>
<body>
<div id="root"></div>
</body>
</html>

创建 users.jsonposts.json 存放模拟请求数据

data/users.json

1
2
3
4
5
6
7
8
9
10
11
12
[
{
"name": "Sanonz",
"email": "sanonz@126.com"
}, {
"name": "Toni Schneider",
"email": "t@toni.org"
}, {
"name": "Beau Lebens",
"email": "beau@dentedreality.com.au"
}
]

data/posts.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
[
{
"id": 1,
"type": "primary",
"text": "This is a primary alert—check it out!",
"isLike": false
},
{
"id": 2,
"type": "secondary",
"text": "This is a secondary alert—check it out!",
"isLike": true
},
{
"id": 3,
"type": "success",
"text": "This is a success alert—check it out!",
"isLike": false
},
{
"id": 4,
"type": "danger",
"text": "This is a danger alert—check it out!",
"isLike": false
},
{
"id": 5,
"type": "warning",
"text": "This is a warning alert—check it out!",
"isLike": false
},
{
"id": 6,
"type": "info",
"text": "This is a info alert—check it out!",
"isLike": false
}
]

创建 user.jsposts.js Reducer

providers/user.js

1
2
3
4
5
6
7
import ReduxStoreProvider from 'redux-store-provider';

import users from './data/users.json';


export default new ReduxStoreProvider({ key: 'USER' })
.setInitialState({value: users[0]});

providers/posts.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import lodash from 'lodash';
import ReduxStoreProvider from 'redux-store-provider';


export new ReduxStoreProvider({ key: 'POST', type: 'list' })
.begin('like')
// 定义触发 Reducer 的 Action
.action((type, value) => {
return {
type,
value,
};
})
// 定义 Reducer
.reducer((type, state, action) => {
let newState = state;
let index = state.list.findIndex(row => row === action.value);
if (!!~index) {
newState = lodash.cloneDeep(state);
const row = newState.list[index];
row.isLike = !row.isLike;
}

return newState;
})
.end();

创建 users.jsposts.js Action

actions.js

1
2
3
4
5
6
import UserStore from '../providers/user';
import PostsStore from '../providers/posts';


export const userAction = UserStore.getAction();
export const postsAction = PostsStore.getAction();

创建 UI 组件

components/Header.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import React from 'react';
import md5 from 'blueimp-md5';
import { connect } from 'react-redux';


@connect(state => ({userStore: state.userStore}))
export default class Header extends React.Component {

render() {
const { userStore } = this.props;

return (
<nav className="navbar navbar-dark bg-dark">
<div className="container">
<a className="navbar-brand" href="#">
ReduxStoreProvider
</a>
<span className="navbar-text">
<img
className="d-inline-block align-middle"
src={`https://www.gravatar.com/avatar/${md5(userStore.value.email)}?s=40`}
width="20"
height="20"
/>
&ensp;
<span className="d-inline-block align-middle">{userStore.value.name}</span>
</span>
</div>
</nav>
);
}

};

components/Posts.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import React from 'react';
import { connect } from 'react-redux';

import { postsAction } from './actions.js';


connect(state => ({userStore: state.userStore, postsStore: state.postsStore}))
export default class Posts extends React.Component {

componentDidMount() {
const { dispatch } = this.props;

// 模拟HTTP请求数据
fetch('./data/post.json')
.then(response => response.json())
.then(rows => {
// 请求的数据填充到 PostsStore 中
dispatch(postsAction.fill(rows);
}));
}

onLike(e, row) {
const { dispatch } = this.props;
dispatch(postAction.like(row));
}

onRemove(e, index) {
const { dispatch } = this.props;
dispatch(postAction.remove(index));
}

render() {
const { postsStore } = this.props;

return (
<div>
{postsStore.list.map((row, index) =>
<div
key={row.id}
className={`alert alert-${row.type}`}
role="alert"
>
{row.text}
&ensp;
<button
type="button"
className={`btn btn-sm btn-${row.isLike ? 'success' : 'light'}`}
onClick={e => this.onLike(e, row)}
>
Like
</button>
<button
type="button"
className="close"
aria-label="Close"
onClick={e => this.onRemove(e, index)}
>
<span aria-hidden="true">&times;</span>
</button>
</div>
)}
</div>
);
}

};

App.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import React from 'react';
import lodash from 'lodash';
import { connect } from 'react-redux';

import Header from './components/Header';
import Posts from './components/Posts';
import { userAction, postsAction } from './actions';
import users from './data/users.json';


@connect(state => ({userStore: state.userStore, postsStore: state.postsStore}))
export default class App extends React.Component {

onRandomUser() {
const { dispatch, userStore } = this.props;
const data = users.filter(user => user.email !== userStore.value.email);
dispatch(userAction.merge(lodash.sample(data)));
}

onShufflePosts() {
const { dispatch, postsStore } = this.props;
dispatch(postsAction.fill(lodash.shuffle(postsStore.list)));
}

onAddPost() {
const { dispatch, postsStore } = this.props;
dispatch(postAction.push(lodash.sample(postsStore.list)));
// dispatch(postAction.unshift(lodash.sample(postsStore.list)));
}

render() {
return (
<div className="app">
<Header />
<div className="container">
<div className="content">
<div className="row">
<div className="col-sm-4">
<div className="list-group">
<button
type="button"
className="list-group-item list-group-item-action"
onClick={e => this.onAddPost(e)}
>
Add Post
</button>
<button
type="button"
className="list-group-item list-group-item-action"
onClick={e => this.onShufflePosts(e)}
>
Shuffle Posts
</button>
<button
type="button"
className="list-group-item list-group-item-action"
onClick={e => this.onRandomUser(e)}
>
Random User
</button>
</div>
</div>
<div className="col-sm-8">
<Posts />
</div>
</div>
</div>
</div>
</div>
);
}

}

创建 APP

setup.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import App from './App';

import UserStore from './providers/user';
import PostsStore from './providers/posts';


const store = Redux.createStore(
Redux.combineReducers({
userStore: UserStore.getReducer(),
postsStore: PostsStore.getReducer(),
})
);

ReactDOM.render(
<ReactRedux.Provider store={store}>
<App />
</ReactRedux.Provider>,
document.getElementById('root')
);