【重点突破】—— Redux基础&进阶:用好Redux必备(一)

前言:正在学习react大众点评项目课程的todoList应用阶段,学习react、redux、react-router基础知识。


一、Redux基础

1.Why Redux

  • 复杂的状态:API数据、本地数据、UI状态等
  • 视图和状态管理耦合,状态管理失控
  • 视图层:React状态管理层:Redux

  

2.state

  • 集中管理,全局唯一
  • 不可变性
  • 定义方式同React State

3.Actions

  • 描述如何修改状态
  • JSON对象,type属性必需
  • 发送:store.dispatch

4.Reducer

5.Reducer拆分

  • 便于维护和扩展
  • 合并API: combineReducers

6.Store

7.react-redux

  • 向根组件注入Store -> Provider组件
  • 连接React组件和Redux状态层 -> connect
  • 获取React组件所需的State和Actions -> map api(mapStateToProps,mapDispatchToProps

8.展示型组件和容器型组件

9.容器型编写

  • 本质上是react的高阶组件
    //AddTodoContainer.js
    import { connect } from "react-redux";
    import { setTodoText, addTodo } from "../actions";
    import AddTodo from "../components/AddTodo";
    
    const mapStateToProps = state => ({
      text: state.text
    });
    
    const mapDispatchToProps = dispatch => ({
      setTodoText: text => dispatch(setTodoText(text)),
      addTodo: text => dispatch(addTodo(text))
    });
    
    export default connect(
      mapStateToProps,
      mapDispatchToProps
    )(AddTodo);
    
    //FooterContainer.js
    import { connect } from "react-redux";
    import { setFilter } from "../actions";
    import Footer from "../components/Footer";
    
    const mapStateToProps = state => ({
      filter: state.filter
    });
    
    const mapDispatchToProps = dispatch => ({
      setFilter: filter => dispatch(setFilter(filter))
    });
    
    export default connect(
      mapStateToProps,
      mapDispatchToProps
    )(Footer);
    
    //TodoListContainer.js
    import { connect } from "react-redux";
    import { toggleTodo } from "../actions";
    import TodoList from "../components/TodoList";
    
    const getVisibleTodos = (todos, filter) => {
      switch (filter) {
        case "all":
          return todos;
        case "completed":
          return todos.filter(t => t.completed);
        case "active":
          return todos.filter(t => !t.completed);
        default:
          return new Error("Unknown filter: " + filter);
      }
    };
    
    const mapStateToProps = state => ({
      todos: getVisibleTodos(state.todos, state.filter)
    });
    
    const mapDispatchToProps = dispatch => ({
      toggleTodo: id => dispatch(toggleTodo(id))
    });
    
    export default connect(
      mapStateToProps,
      mapDispatchToProps
    )(TodoList);
  • 修改App.js
    import React, { Component } from "react";
    import AddTodoContainer from "../containers/AddTodoContainer";
    import TodoListContainer from "../containers/TodoListContainer";
    import FooterContainer from "../containers/FooterContainer";
    
    class App extends Component {
     
      render() {
        return (
          <div>
            <AddTodoContainer/>
            <TodoListContainer/>
            <FooterContainer/>
          </div>
        );
      }
    }
    
    export default App;  
  • 改造AddTodo.js、Footer.js、TodoList.js
    //AddTodo.js
    import React, { Component } from 'react';
    
    class AddTodo extends Component {
      
      render() {
        return (
          <div>
            <input value={this.props.text} onChange={this.handleChange}/>
            <button onClick={this.handleClick}>Add</button>
          </div>
        );
      }
    
      handleChange = (e) => {
        this.props.setTodoText(e.target.value)
      } 
    
      handleClick = () => {
        this.props.addTodo(this.props.text);
      }
    }
    
    export default AddTodo;
    
    //Footer.js
    import React, { Component } from "react";
    
    class Footer extends Component {
      render() {
        const { filter, setFilter: setVisibilityFilter } = this.props;
        return (
          <div>
            <span>Show:</span>
            <button
              disabled={filter === "all"}
              onClick={() => setVisibilityFilter("all")}
            >
              All
            </button>
            <button
              disabled={filter === "active"}
              onClick={() => setVisibilityFilter("active")}
            >
              Active
            </button>
            <button
              disabled={filter === "completed"}
              onClick={() => setVisibilityFilter("completed")}
            >
              Completed
            </button>
          </div>
        );
      }
    }
    
    export default Footer;
    
    //TodoList.js
    import React, { Component } from 'react';
    import Todo from "./Todo";
    
    class TodoList extends Component {
      render() {
        const {todos, toggleTodo} = this.props;
        return (
          <ul>
            {
              todos.map(todo => {
                return <Todo key={todo.id} {...todo} 
                onClick={() => {toggleTodo(todo.id)}}/>
              })
            }
          </ul>
        );
      }
    }
    
    export default TodoList;
    
    //Todo.js
    import React, { Component } from "react";
    
    class Todo extends Component {
      render() {
        const { completed, text, onClick } = this.props;
        return (
          <li
            onClick={onClick}
            style={{
              textDecoration: completed ? "line-through" : "none"
            }}
          >
            {text}
          </li>
        );
      }
    }
    
    export default Todo;  
  • index.js中: createStore、Provider、rootReducer
    import React from "react";
    import ReactDOM from "react-dom";
    import { createStore } from "redux";
    import { Provider } from "react-redux";
    import rootReducer from "./reducers";
    import App from "./components/App";
    
    const store = createStore(rootReducer);
    
    ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
      document.getElementById("root")
    );

10.回顾React组件和Redux的连接过程

  • 将组件划分为容器型组件(containers)和展示型组件(components)
  • 展示型组件:负责UI的展现,不关心数据从何而来,也不关心如何修改数据
  • 容器型组件:关注逻辑的实现,关注从何获取数据,应该如何去修改数据
  • 尽量在较低层级的组件进行连接,以减少不必要的组件渲染 (排除app.js)
  • 如果展示型组件(例如TodoList组件)不存在复用的场景,只会作为容器型组件而存在时,可以直接定义在containners目录下

11.异步Action(调用api的Action)

//actionTypes.js

export const ADD_TODO = 'ADD_TODO'
export const TOGGLE_TODO = 'TOGGLE_TODO'
export const SET_TODO_TEXT = 'SET_TODO_TEXT'
export const SET_FILTER = 'SET_FILTER'
export const FETCH_TODOS_REQUEST = 'FETCH_TODOS_REQUEST'
export const FETCH_TODOS_SUCCESS = 'FETCH_TODOS_SUCCESS'
export const FETCH_TODOS_FAILURE = 'FETCH_TODOS_FAILURE'
//actions->index.js

import {ADD_TODO, TOGGLE_TODO, SET_TODO_TEXT, SET_FILTER, FETCH_TODOS_REQUEST, FETCH_TODOS_SUCCESS, FETCH_TODOS_FAILURE} from './actionTypes'

let nextTodoId = 0

const fetchTodosRequest = () => ({
  type: FETCH_TODOS_REQUEST
})

const fetchTodosSuccess = (data) => ({
  type: FETCH_TODOS_SUCCESS,
  data
})

const fetchTodosFailure = (error) => ({
  type: FETCH_TODOS_FAILURE,
  error
})

/**
 * 获取待办事项初始数据
 */
export const fetchTodos = () => {

  //异步action 返回的是一个函数
  return (dispatch) => {
    dispatch(fetchTodosRequest());
    return fetch("./mock/todos.json").then(
      response => {
        response.json().then(data => {
          dispatch(fetchTodosSuccess(data));
        }) 
      },
      error => {
        dispatch(fetchTodosFailure(error));
        console.log("An error occurred: "+ error)
      }
    )
  }
}

/**
 * 新增待办事项
 * @param {*} text 
 */
export const addTodo = (text) => ({
  type: ADD_TODO,
  id: nextTodoId++,
  text
})

/**
 * 更改待办事项状态
 * @param {*} id 
 */
export const toggleTodo = id => ({
  type: TOGGLE_TODO,
  id
})

/**
 * 设置过滤状态
 * @param {*} filter 
 */
export const setFilter = filter => ({
  type: SET_FILTER,
  filter
})

/**
 * 设置新增待办事项的文本
 * @param {*} text 
 */
export const setTodoText = text => ({
  type: SET_TODO_TEXT,
  text
}) 

12.redux-thunk中间件

  • 可以处理异步Action的中间件
//src->index.js

import { createStore, applyMiddleware } from "redux";
import thunkMiddleware from "redux-thunk";

const store = createStore(rootReducer, applyMiddleware(thunkMiddleware));
  • TodoList.js获取初始数据
componentDidMount() {
       this.props.fetchTodos();
}
  • TodoListContainer.js
import { toggleTodo, fetchTodos } from "../actions";

const mapStateToProps = state => ({
     todos: getVisibleTodos(state.todos.data, state.filter)
});


const mapDispatchToProps = dispatch => ({
     toggleTodo: id => dispatch(toggleTodo(id)),
     fetchTodos: () => dispatch(fetchTodos())
});

14.Redux调试工具——Redux DevTools

  • 支持Crome浏览器和Firefox浏览器
//src->index.js
import { createStore, applyMiddleware, compose } from "redux";

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(rootReducer, composeEnhancers(applyMiddleware(thunkMiddleware)));

注:项目来自慕课网  

人与人的差距是:每天24小时除了工作和睡觉的8小时,剩下的8小时,你以为别人都和你一样发呆或者刷视频
原文地址:https://www.cnblogs.com/ljq66/p/14376041.html