react-redux使用入门
什么是redux?
redux
是项目的状态管理库,对于大的项目尤其有用,和react
本身并不相关,可以和任何框架融合。对于一个项目而言,主要的数据可能是这样的:
state = {
todos: [{
text: 'Eat food',
completed: true
}, {
text: 'Exercise',
completed: false
}],
visibilityFilter: 'SHOW_COMPLETED'
}
这样的数据在各个组件之间共享,想要改变state
中的值,要通过一种触发action
的方式实现,一个action
就是这样的一个对象:
{ type: 'ADD_TODO', text: 'Go to swimming pool' }
{ type: 'SET_VISIBILITY_FILTER', filter: 'SHOW_ALL' }
而reducer
就是接收这样的action
,然后返回新的state
值的函数,像下面这样:
function visibilityFilter(state = 'SHOW_ALL', action) {
if (action.type === 'SET_VISIBILITY_FILTER') {
return action.filter;
} else {
return state;
}
}
function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return state.concat([{ text: action.text, completed: false }]);
case 'TOGGLE_TODO':
return state.map((todo, index) =>
action.index === index ?
{ text: todo.text, completed: !todo.completed } :
todo
)
default:
return state;
}
}
这就是redux
的基本思想。
至于redux
的基本API,这里不做介绍。
下面大家可以跟着我一步一步来在一个react项目中使用redux,加粗部分才是需要的代码哦。
首先需要安装redux
和react-redux
npm install redux
npm install react-redux
建立redux.js文件
然后,我们可以先设计一下项目的state
// redux.js
const initialState = {
searchHistory: ['大码女鞋','男装保暖加厚','冰箱三开门']
}
日后我们要定义的action
可能是长这样的:
{
type: ADD_TO_SEARCH_HISTORY,
payload: '男装'
}
编写一个reducer
来处理这个searchHistory
:
// redux.js
const handleSearchHistory = (state = initialState, action) => {
console.log(action);
switch (action.type) {
case ADD_TO_SEARCH_HISTORY:
return
{...state, searchHistory:state.searchHistory.concat([action.payload])};
default:
return state;
}
}
下面,调用redux
的combineReducers
// redux.js
let rootReducers = combineReducers({
handleSearchHistory: handleSearchHistory
})
再利用redux
的createStore
来创立一个store
// redux.js
let store = createStore(rootReducers);
export default store;
到这里我们都还没有用到react-redux
,下面开始使用
建立search.js文件,在其中编写一个简单的SearchComponent
组件
// search.js
class SearchComponent extends React.Component {
constructor() {
super();
this.state = {
searchHistory: []
}
}
render() {
return (
<div>
{this.state.searchHistory.map((item, index) => <div key={index}>{item}</div>)}
</div>
)
}
}
export default SearchComponent;
如何将SearchComponent
和store
建立联系呢?
在项目的根节点文件中像这样把store
传递给所有的组件:
Provider
是react-redux
的api,可以把store
传递给所有的组件。
import { Provider } from 'react-redux'
ReactDOM.render(
<Provider store={store}>
<App />,
</Provider>
document.getElementById('root')
);
回到search.js
,先把下面的代码写入,我们后面再解释:
// search.js
const mapStateToProps = (state) => {
return {
state: state
}
}
const Search = connect(mapStateToProps)(SearchComponent);
export default Search;
connect
是react-redux
的api,是这样的形式
connect(mapStateToProps, mapDispatchToProps)(SearchComponent)
其中,mapStateToProps
用来把项目的state
传给props
,mapDispatchToProps
可以把dispatch
传给props
,我们暂时可以不用这个参数,只用前一个参数。
之后,可以在 search.js 中访问到项目的state
,以下面的方式来访问
this.props.state.handleSearchHistory
注意这里的handleSearchHistory
是上面我们编写reducer
的时候,命名的属性值哦。
let rootReducers = combineReducers({
handleSearchHistory: handleSearchHistory
})
然后我们来修改 search.js
// search.js
class SearchComponent extends React.Component {
constructor() {
super();
}
render() {
return (
<div>
{this.props.state.handleSearchHistory.searchHistory
.map((item, index) => <div key={index}>{item}</div>)}
</div>
)
}
}
const mapStateToProps = (state) => {
return {
state: state
}
}
const Search = connect(mapStateToProps)(SearchComponent);
export default Search;
此时的 redux.js 文件应该是这样的:
// redux.js
import { combineReducers, createStore } from 'redux';
const initialState = {
searchHistory: ['大码女鞋','男装保暖加厚','冰箱三开门']
}
// action.payload是一个字符串
const handleSearchHistory = (state = initialState, action) => {
console.log(action);
switch (action.type) {
case ADD_TO_SEARCH_HISTORY:
return
{...state, searchHistory:state.searchHistory.concat([action.payload])};
default:
return state;
}
}
let rootReducers = combineReducers({
handleSearchHistory: handleSearchHistory
})
let store = createStore(rootReducers);
export default store;
到这里,已经实现了在SearchComponent
中访问项目state
,下面我们再来看看如何在SearchComponent
中来触发一个dispatch
.
修改 search.js
render() {
return (
<div>
{this.props.state.handleSearchHistory.searchHistory
.map((item, index) => <div key={index}>{item}</div>)}
<button onClick={() => this.props.dispatch({
type: ADD_TO_SEARCH_HISTORY,
payload: '男装'
})}></button>
</div>
)
}
这样显示地触发了dispatch
,还记得上面提到的connect
接收的另一个参数mapDispatchToProps
吗?这个时候就用上了,我们来看看。
继续完善 search.js,添加如下代码:
const addSearchHistory = (value) => {
return {
type: ADD_TO_SEARCH_HISTORY,
payload: value
}
}
const mapDispatchToProps = (dispatch) => {
return {
addSearchHistory: (currentValue) => dispatch(addSearchHistory(currentValue)),
deleteSearchHistory: () => dispatch(deleteSearchHistory())
}
}
const Search = connect(mapStateToProps, mapDispatchToProps)(SearchComponent);
export default Search;
之后,我们就可以在 search.js 中像这样触发dispatch
this.props.addSearchHistory('男装');
此时,完整的 search.js 代码如下:
// search.js
class SearchComponent extends React.Component {
constructor() {
super();
}
render() {
return (
<div>
{this.props.state.handleSearchHistory.searchHistory
.map((item, index) => <div key={index}>{item}</div>)}
<button onClick={() => this.props.addSearchHistory('男装')}></button>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
state: state
}
}
const addSearchHistory = (value) => {
return {
type: ADD_TO_SEARCH_HISTORY,
payload: value
}
}
const mapDispatchToProps = (dispatch) => {
return {
addSearchHistory: (currentValue) => dispatch(addSearchHistory(currentValue)),
deleteSearchHistory: () => dispatch(deleteSearchHistory())
}
}
const Search = connect(mapStateToProps, mapDispatchToProps)(SearchComponent)
export default Search;
我们现在只管理了项目中 state
中的 searchHistory
一项数据,如果有多项数据要管理的话,就要编写多个reducer
,写在一个文件里就会臃肿,可以分开到多个文件中;像 search.js 中的 addSearchHistory
方法也可以写到单独的文件中去。不过,这和技术就无关了,只是项目的优化。