React-Redux

React-Redux

  • 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目录结构

     

 2store.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;

3action.js

function add() {
    return {
        type: 'ADD'
    }
}

function minus() {
    return {
        type: 'MINUS'
    }
}

const action = { add,minus }

export default action;

 

4Counter1.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=> state

const mapDispatchToProps = {
    ...action
}

export default connect(mapStateToProps,mapDispatchToProps)(Counter1);

 

5App.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;

 

6index.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));
    }
}

7about.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.props
        return (
            <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.props
        return (
            <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')
);

 

posted @ 2022-01-21 21:25  #独狼  阅读(160)  评论(0编辑  收藏  举报