前言:正在学习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网上应用店下载:https://www.gugeapps.net/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd#download
- 支持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)));
注:项目来自慕课网