一、简介
React-Redux 是 Redux 的官方 React 绑定库。React-Redux 能够使React组件从Redux store中读取数据,并且向 store 分发 actions 以更新数据。React-Redux 并不是 Redux 内置,需要单独安装。React-Redux 一般会和 Redux 结合一起使用。二、安装redux、react-redux
npm install redux
npm install react-redux
三、Provider 和 connect
Provider:它是react-redux 提供的一个 React 组件,作用是把state传给它的所有子组件,也就是说 当你用Provider传入数据后 ,下面的所有子组件都可以共享数据,十分的方便。
Provider的使用方法是:用Provider组件包裹在最外层的组件
<Provider store={ store }>
<App />
</Provider>
注意:一定是在Provider中传store
connect:它是一个高阶组件 所谓高阶组件就是给它传入一个组件,它会返回新的加工后的组件,connect 方法有四个参数([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])后面两个参数可以不写,不写的话它是有默认值的。主要关注前两个参数:mapStateToProps和 mapDispatchToProps;
mapStateToProps:如果定义该参数,组件将会监听 Redux store 的变化。任何时候,只要 Redux store 发生改变,mapStateToProps 函数就会被调用。mapDispatchToProps:如果传递的是一个对象,那么每个定义在该对象的函数都将被当作 Redux action creator,对象所定义的方法名将作为属性名;每个方法将返回一个新的函数,函数中dispatch方法会将 actioncreator 的返回值作为参数执行。这些属性会被合并到组件的 props 中。
connect的使用方法是:把指定的state和指定的action与React组件连接起来,后面括号里面写UI组件名
connect(mapStateToProps,mapDispatchToProps)(TbItem)
四、示例:
例1:利用react-redux实现计数器
1、目录结构
2、store.js
import {createStore} from "redux";export const ADD = 'ADD';export const MINUS = 'MINUS';function reducer (state= {count:0},action){console.log('Action:',action);switch (action.type) {case ADD:return { count: state.count + 1}case MINUS:return { count: state.count - 1}default:return state}}let store = createStore(reducer);export default store;
3、action.js
function add() {return {type: 'ADD'}}function minus() {return {type: 'MINUS'}}const action = { add,minus }export default action;
4、Counter1.js
import React,{ Component } from "react";import { connect } from "react-redux";import action from "../actions/action";class Counter1 extends Component {render() {return (<div><p>{ this.props.count }</p><button onClick={this.props.add }>Count++</button> <button onClick={ this.props.minus}>count--</button></div>)}}const mapStateToProps = state=> stateconst mapDispatchToProps = {...action}export default connect(mapStateToProps,mapDispatchToProps)(Counter1);
5、App.js
import logo from './logo.svg';import './App.css';import Counter1 from "./components/Counter1";function App() {return (<div className="App"><header className="App-header"><img src={logo} className="App-logo" alt="logo" /><Counter1 /></header></div>);}export default App;
6、index.js
import React from 'react';import ReactDOM from 'react-dom';import './index.css';import App from './App';import { Provider } from "react-redux";import store from "./store/store";ReactDOM.render(<Provider store={ store }><App /></Provider>,document.getElementById('root'));
action:行为 它是一个对象 里面必有type来指定其类型 这个类型可以理解为你要做什么,reducer要根据action的type来返回不同的state 每个项目有且可以有多个action。它是 store 数据的唯一来源。通过 store.dispatch() 将 action 传到 store。记住 actions 只是描述了有事情发生了这一事实,并没有描述应用如何更新 state。
reducer:可以理解为一个专门处理state的工厂,给它一个旧数据它会根据不同action.type返回新的数据,每个项目有且可以有多个reducer。reducers 指定了应用状态的变化如何响应 actions 并发送到 store 的。
store:store本质上是一个状态树,保存了所有对象的状态。任何UI组件都能直接的从store访问特定对象的状态。每个项目有且只能有一个store。
提供 getState() 方法获取 state;提供 dispatch(action) 方法更新 state;通过 subscribe(listener) 注册监听器;
例2:多组件共享store
(1)constants.js(定义公共方法名)
export const ADD_NUMBER = 'ADD_NUMBER'export const SUB_NUMBER = 'SUB_NUMBER'
(2)actionCreators.js(专门存放方法名)
import {ADD_NUMBER,SUB_NUMBER} from "./constants";export const addAction = num => ({type: ADD_NUMBER,num})export const subAction = num => ({type: SUB_NUMBER,num})
(3)reducer.js(存放事件)
import {ADD_NUMBER,SUB_NUMBER} from "../action/constants";const deflateState = {counter: 0}function reducer(state = deflateState,action){switch (action.type){case ADD_NUMBER:return {...state,counter: state.counter + action.num }case SUB_NUMBER:return {...state,counter: state.counter - action.num }default:return state;}}export default reducer;
(4)store.js文件
import { createStore } from "redux";import reducer from "../reducer/reducer";const store = createStore(reducer);export default store;
(5)home.js(Home组件)
import React,{ Component } from "react";import store from "../redux/store/store";import { addAction } from "../redux/action/actionCreators";export default class Home extends Component {constructor(props) {super(props);this.state = {counter: store.getState().counter}}//订阅componentDidMount() {this.unSubscribe = store.subscribe(()=>{this.setState({counter: store.getState().counter})})}//取消订阅componentWillUnmount() {this.unSubscribe();}render() {return (<div><h2>Home</h2><h3>当前: { this.state.counter }</h3><button onClick={ event => this.increment() }>+1</button><button onClick={ event => this.addNumber(5) }>+5</button></div>)}increment(){store.dispatch(addAction(1));}addNumber(num){store.dispatch(addAction(num));}}
(7)about.js(About组件)
import React,{ Component } from "react";import store from "../redux/store/store";import { addAction } from "../redux/action/actionCreators";export default class Home extends Component {constructor(props) {super(props);this.state = {counter: store.getState().counter}}//订阅componentDidMount() {this.unSubscribe = store.subscribe(()=>{this.setState({counter: store.getState().counter})})}//取消订阅componentWillUnmount() {this.unSubscribe();}render() {return (<div><h2>Home</h2><h3>当前: { this.state.counter }</h3><button onClick={ event => this.increment() }>+1</button><button onClick={ event => this.addNumber(5) }>+5</button></div>)}increment(){store.dispatch(addAction(1));}addNumber(num){store.dispatch(addAction(num));}}
(8)App.js
import logo from './logo.svg';import './App.css';import Home from "./components/home";import About from "./components/about";function App() {return (<div className="App"><header className="App-header"><img src={logo} className="App-logo" alt="logo" /><Home /><About/></header></div>);}export default App;
(9)index.js
import React from 'react';import ReactDOM from 'react-dom';import './index.css';import App from './App';import store from "./redux/store/store";import { Provider } from "react-redux";ReactDOM.render(<Provider store={store}><App /></Provider>,document.getElementById('root'));
例3、合并多个reducer
(1)目录结构
(2)action文件
2.1、changeAge_action.js
export const CLICK_ADD = 'CLICK_ADD';export const CLICK_SUB = 'CLICK_SUB';export function clickAdd() {return {type: CLICK_ADD}}export function clickSub() {return {type: CLICK_SUB}}
2.2、inputInfo.js
export const INPUT_INFO = 'INPUT_INFO';export function inputInfo(name){return {type: INPUT_INFO,name}}
(3)reducer文件
3.1、changeAge_reducer.js
import { CLICK_ADD,CLICK_SUB } from "../actions/changeAge_action";function initialState() {return {age: 20};}function changeAge(state=initialState(),action) {switch (action.type) {case CLICK_ADD: {return {...state,age: state.age + 1}}case CLICK_SUB: {return {...state,age: state.age - 1}}default:return state;}}export default changeAge;
3.2、inputInfo_reducer.js
import { INPUT_INFO } from "../actions/inputInfo_action";function initialState() {return {name: "ZhangSan"}}function inputInfo(state= initialState(),action) {switch (action.type) {case INPUT_INFO: {return {...state,name: action.name}}default:return state;}}export default inputInfo;
3.3、rootReducer.js
(4)store.js文件
import { createStore } from "redux";import rootReducer from "../reducer/rootReducer";const store = createStore(rootReducer);export default store;
(5)组件
5.1、Name组件(name.js)
import React,{ Component } from "react";import PropTypes from "prop-types";import { connect } from "react-redux";class Name extends Component {render() {const { name } = this.propsreturn (<div>My name is <i>{ name }</i></div>)}}Name.propTypes = {name: PropTypes.string.isRequired}const mapStateToProps = (state)=> {return {name: state.Name_store.name}}export default connect(mapStateToProps)(Name);
5.2、Age组件(age.js)
import React,{ Component } from "react";import PropTypes from "prop-types";import { connect } from "react-redux";import {clickAdd,clickSub } from "../actions/changeAge_action";class Age extends Component{constructor(props) {super(props);this.clickAdd = this.clickAdd.bind(this);this.clickSub = this.clickSub.bind(this);}render() {const { age } = this.propsreturn (<div>I am <i>{ age } </i>years old this year <button onClick={this.clickAdd }>Add Age</button><button onClick={this.clickSub }>Sub Age</button></div>)}clickAdd(event) {this.props.dispatch(clickAdd());}clickSub(event) {this.props.dispatch(clickSub());}}Age.propTypes = {age: PropTypes.number.isRequired}const mapStateToProps = (state) => {return {age: state.Age_store.age}}export default connect(mapStateToProps)(Age);
5.3、Form组件(form.js)
import React,{Component} from "react";import {connect} from "react-redux";import { inputInfo } from "../actions/inputInfo_action";class Form extends Component {constructor(props) {super(props);this.clickSubmit = this.clickSubmit.bind(this);}render() {return (<div><input type={"text"} placeholder={"name"} id={"nameInput"}/><button onClick={ this.clickSubmit }>提交</button></div>)}clickSubmit(event) {let name = document.getElementById('nameInput').value;console.log(name);this.props.dispatch(inputInfo(name));}}export default connect()(Form);
(6)App.js
import logo from './logo.svg';import './App.css';import Name from "./redux/components/name";import Age from "./redux/components/age";import Form from "./redux/components/form";function App() {return (<div className="App"><header className="App-header"><img src={logo} className="App-logo" alt="logo" /><Name /><br/><Age /><br/><Form /></header></div>);}export default App;
(7)index.js
import React from 'react';import ReactDOM from 'react-dom';import './index.css';import App from './App';import { Provider } from "react-redux";import store from "./redux/store/store";ReactDOM.render(<Provider store={ store }><App /></Provider>,document.getElementById('root'));
运行效果: