前言:正在学习react大众点评项目课程的todoList应用阶段,学习react、redux、react-router基础知识。
一、Redux基础
1.Why Redux
- 复杂的状态:API数据、本地数据、UI状态等
- 视图和状态管理耦合,状态管理失控
- 视图层:React状态管理层:Redux
![](https://img-blog.csdnimg.cn/img_convert/38ba8d2b8869b9f1fad468ffd8047eaa.png)
2.state
- 集中管理,全局唯一
- 不可变性
- 定义方式同React State
3.Actions
- 描述如何修改状态
- JSON对象,type属性必需
- 发送:store.dispatch
4.Reducer
![](https://img-blog.csdnimg.cn/img_convert/58c9ef95c6d7d20fc7598f89b3fac610.png)
5.Reducer拆分
- 便于维护和扩展
- 合并API: combineReducers
6.Store
![](https://img-blog.csdnimg.cn/img_convert/058e69564acc2b5de3a9c061638f485e.png)
7.react-redux
- 向根组件注入Store -> Provider组件
- 连接React组件和Redux状态层 -> connect
- 获取React组件所需的State和Actions -> map api(mapStateToProps,mapDispatchToProps)
8.展示型组件和容器型组件
![](https://img-blog.csdnimg.cn/img_convert/49d552e4d2c12c0bc8dbbbe27de62656.png)
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)));
注:项目来自慕课网