上一节——redux基础知识中介绍了redux
的基本使用。
但是在实际react项目中使用redux
的话,一般不会使用原生redux
,而是会再加上react-redux
一起使用。
redux
与react-redux
并没有什么关系,redux
可以在任意地方使用,可以在react
项目中使用,也可以在vue
项目中使用,甚至可以在jQuery
项目中使用。而react-redux
是redux官方做的用于更好地与react
进行配合。
这一节,将会在前一节redux
的基础上,使用react-redux
来让我们的组件更丝滑。
react-redux
可以理解为是redux
关于react
项目的高级语法糖,他并不是redux
,只是让你的redux
代码在react
项目中更丝滑。
react-redux
中有两个重要的成员:
下面将会详细介绍一下这两部分。
Provider
是一个标签,他是一个react-redux
封装的标签,用来包裹根组件用的,他的作用就是可以让你的整个应用都能随时随地的拿到store
中的state
。
Provider
标签有一个参数,props
参数需要将store
传递进去,然后通过context往下传递,使每个组件都能拿到store
。
import { Provider } from 'react-redux';
import { Provider } from 'react-redux';
ReactDOM.render(
<Provider>
<App />
</Provider>
);
import { Provider } from 'react-redux';
import store from './store';
ReactDOM.render(
<Provider props={store}>
<App />
</Provider>
);
虽然根节点外使用了Provider
标签进行了包裹,但是Provider
只是提供一个将store
往下传递的能力,想要使用到store
中的数据,就必须使用connect()
方法对组件进行包裹(加强)。
import { connect } from 'react-redux';
注意:调用connect()方法将会返回另一个方法,此时再将组件作为参数传入该方法进行包裹。
import { connect } from 'react-redux';
// 引入组件
import ComA from './ComA';
// 使用connect包裹组件
connect()(ComA);
这里使用了函数柯里化的概念,这种写法常用的地方是在工厂函数,如果有了解过express
/koa
/egg.js
中间件的写法,应该就会对该种写法比较熟悉。
前面一部分connect()
通过传入参数具体定制工厂函数的工作逻辑/模式,并返回一个函数,然后再将需要传入工厂函数的参数传入,运行工厂函数。
这里第一部分connect()
方法的参数如下(按顺序):
参数名 | 类型 | 说明 |
---|---|---|
mapstateToProps(state, ownProps) | Function | 该函数会将store的数据绑定到组件上。state : redux中的store ownProps : 组件自己的props |
mapDispatchToProps(dispatch, ownProps) | Function | 该函数返回一个对象,该对象的每个属性就是一个action,会将这些action作为props绑定到组件上。dispatch : 就是store.dispatchownProps : 组件自己的props |
mergeProps(stateProps, dispatchProps, ownProps) | Function | 不管是stateProps 还是dispatchProps ,都需要和ownProps merge之后才会被赋给我们的组件,通常情况下,你可以不传这个参数,connect就会使用object.assign 替代该方法 |
options | Object | connect的其他配置项 |
其中,mapstateToProps
和mapDispatchToProps
是需要特别掌握的,这两个在开发过程中使用的频率最高也是最重要的部分。
import { connect } from 'react-redux';
import ComA from './ComA';
// 定义mapstateToProps
const mapstateToProps = (state, ownProps) => {
// ...
}
// 定义mapDispatchToProps
const mapDispatchToProps = (dispatch, ownProps) => {
// ...
}
// 使用connect包裹组件
connect(mapstateToProps, mapDispatchToProps)(ComA);
其实也想把mapstateToProps
和mapDispatchToProps
使用示例贴上来,但是好像脱离实际使用的这部分代码反而让人看得云里雾里的。不如我们直接上项目吧。
本项目还是用一个计数器来进行展示吧。包含两个组件
老样子,还是使用CRA
工具来创建一个react项目:
# 创建项目
npx create-react-app react_redux_demo
# 进入项目目录
cd react_redux_demo
所需安装的依赖有两个:
npm i redux react-redux --save
和上一节一样的操作,在src
目录下添加一个文件夹src/reducer
,里面新建一个index.js
文件。
src/reducer/index.js
文件内容// 设置一个默认的state
const initState = {
value: 0
};
const reducer = (state = initState, action) => {
switch(action.type) {
case 'add':
return { value: state.value + 1 };
case 'decrese':
return { value: state.value - 1 };
default:
return state;
}
}
export default reducer;
和上一节一样的操作,在src
目录下添加一个文件夹src/store
,里面新建一个index.js
文件。
src/store/index.js
文件内容import { createStore } from 'redux';
import reducer from '../reducer';
const store = createStore(reducer);
export default store;
因为根节点是在src/index.js
文件中,所以直接到这里来进行一个Provider
的包裹就行。当然,src/app.js
中包裹也是没有问题的,但是要保证你的Provider
标签把你要用到store
的部分完全包裹起来。
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
// 导入Provider
import { Provider } from 'react-redux';
// 导入store
import store from './store';
// 使用Provider标签包裹根组件
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
按钮组件包含以下几部分:
store
)在src
目录下新建一个Components/Button
文件夹,并在其下创建index.js
文件夹
src/Components/Button/index.js
文件基本内容function Button(props) {
return (
<>
<button> + </button>
<br />
<button> - </button>
</>
);
}
export default Button;
import { connect } from 'react-redux';
function Button(props) {
return (
<>
<button> + </button>
<br />
<button> - </button>
</>
);
}
// 定义mapDispatchToProps
const mapDispatchToProps = (dispatch) => {
// 返回一个对象,该对象每个方法就是一个action,所有的这些action都将绑定到组件的props参数上
return {
add: () => {
dispatch({ type: 'add' });
},
decrese: () => {
dispatch({ type: 'decrese' });
}
};
};
// 使用connect包裹/增强组件
// 这里不需要该组件获取store中的值,故第一个参数mapstateToProps就无需定义
export default connect(null, mapDispatchToProps)(Button);
import { connect } from 'react-redux';
function Button(props) {
// 计数+1事件
const addHandler = () => {
props.add();
};
// 计数-1事件
const decreseHandler = () => {
props.decrese();
};
return (
<>
<button onClick={addHandler}> + </button>
<br />
<button onClick={decreseHandler}> - </button>
</>
);
}
// 定义mapDispatchToProps
const mapDispatchToProps = (dispatch) => {
// 返回一个对象,该对象每个方法就是一个action,所有的这些action都将绑定到组件的props参数上
return {
add: () => {
dispatch({ type: 'add' });
},
decrese: () => {
dispatch({ type: 'decrese' });
}
};
};
// 使用connect包裹/增强组件
// 这里不需要该组件获取store中的值,故第一个参数mapstateToProps就无需定义
export default connect(null, mapDispatchToProps)(Button);
该组件只包含一个功能:
首先,在src
目录下新建一个Components/Counter
文件夹,并在其下创建index.js
文件夹
src/Components/Counter/index.js
文件基本内容function Counter(props) {
return (
<div></div>
);
}
export default Counter;
connect
包裹/增强组件import { connect } from 'react-redux';
function Counter(props) {
return (
<div>{props.value}</div>
);
}
// 定义mapstateToProps,直接将state进行返回
const mapstateToProps = (state) => {
return state;
};
// 使用connect包裹/增强组件
// 该组件不需要发送action,所以无需传入mapDispatchToProps参数
export default connect(mapstateToProps)(Counter);
在src/app.js
中,添加上述完成的两个组件:
import Button from './Components/Button';
import Counter from './Components/Counter';
function App() {
return (
<div className="App">
<Button />
<Counter />
</div>
);
}
export default App;
至此,一个完整的计数器就完成了。