介绍

仓库地址: 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

reducers/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]});

reducers/posts.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import lodash from 'lodash';
import ReduxStoreProvider from 'redux-store-provider';


export new ReduxStoreProvider({ key: 'POST', type: 'list' })
// 扩展更新 Store 方法,LIKE 对应 Action 中的 type 字段
.addHandler('LIKE', (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;
});

创建 users.jsposts.js Action

actions/user.js

1
2
3
4
import UserStore from '../reducers/user';


export default UserStore.createAction();

actions/posts.js

1
2
3
4
5
6
7
8
9
10
11
12
import PostsStore from '../reducers/posts';


export default PostsStore.createActionList({
like: function (value) {
return {
value,
type: PostsStore.type('LIKE'),
// 触发 LIKE 事件,type 和 ReduxStoreProvider:addHandler(key) 的 key 对应
};
},
});

创建 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/posts.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
74
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 from './actions/user.js';
import postsAction from './actions/posts.js';
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 '../reducers/user';
import PostsStore from '../reducers/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')
);