本文目的:因新公司暂无react相关项目安排,防止自己……对某些技术淡忘,所以记录下react项目部分技术要点,方便日后快速进入状态。
Redux 是 JavaScript 状态容器,提供可预测化的状态管理。
Redux 的诞生主要为了解决下面的问题:
1、复杂的数据,比如api数据、本地数据、UI数据等
2、视图和状态管理的耦合导致的状态管理失控=>解决:redux状态管理、react视图层
那么如何设计和使用redux呢?
一、Redux基本思想
store包含了state,state定义UI,UI触发动作Actions ,Actions 发送到Reducers,Reducers根绝actions的面熟将store的state进行修改.
二、redux基础(以todoList为demo)
state:redux的state是集中管理、全局唯一,不可变性
actions:描述如何修改状态,actions就是JSON对象、必须type属性,发送需要使用store.dispatch
reducer:实质是一个函数,接受state、action进行计算,落实action传递的描述
store,redux的store就是一个粘合剂,将redux中的各个部分粘合在一起
代码示例
1、定义state数据
// const initialState ={
// filter: 'all',
// text: '',
// todos: []
// }
2、添加action事件
import {ADD_TODO, TOGGLE_TODO, SET_FILTER, SET_TODO_TEXT} from "./actionTypes"
let nextTodoId = 0
//新增待办事项
export const addTodo = (text, type, id)=>({
id: nextTodoId++,
type: ADD_TODO,
text
})
//更改待办事项状态
export const toggleTodo = id=>({
type: TOGGLE_TODO,
id
})
//设置过滤状态
export const setFilter = filter=>({
type: SET_FILTER,
filter
})
//设置事项文字
export const setTodoText = text=>({
type: SET_TODO_TEXT,
text
})
4、reducer处理action
import {ADD_TODO, TOGGLE_TODO, SET_FILTER, SET_TODO_TEXT} from "../actions/actionTypes"
const initialState ={
filter: 'all',
text: '',
todos: []
}
export const todoApp = (state,action)=>{
switch(action.type){
case ADD_TODO: //添加待办事项
return {
...state,
todos:[
...state.todos,
{
id: action.id,
text: action.text,
completed: false
}
]
}
case TOGGLE_TODO: //改变事项状态
return {
...state,
todos:state.todos.map(todo=>{
if(todo.id === action.id){
return {
...todo,
completed:!todo.completed
}
}else{
return {
...todo
}
}
})
}
case SET_TODO_TEXT: //设置事项内容
return {
...state,
text:action.text
}
case SET_FILTER: //设置过滤状态
return {
...state,
filter: action.filter
}
default:
return state;
}
}
为了便于维护和拓展,最好拆分一下reducer,可以通过combineReducers完成
import { combineReducers } from 'redux'
import todos from './todo'
import text from './text'
import filter from './filter'
export default combineReducers({
todos,
text,
filter,
})
import {SET_TODO_TEXT} from "../actions/actionTypes"
const text = (state = "",action)=>{ //此处的state非原始initalState,传递进来的是更改的文字text
switch(action.type){
case SET_TODO_TEXT: //添加待办事项
return action.text
default :
return state
}
}
export default text
import { SET_FILTER } from "../actions/actionTypes"
const filter = (state = "all",action)=>{ //此处的state非原始initalState,传递进来的是todos
switch(action.type){
case SET_FILTER: //设置过滤状态
return action.filter
default :
return state
}
}
export default filter
import {ADD_TODO, TOGGLE_TODO} from "../actions/actionTypes"
const todos = (state = [],action)=>{ //此处的state非原始initalState,传递进来的是todos
switch(action.type){
case ADD_TODO: //添加待办事项
return [
...state,
{
text: action.text,
completed: false
}
]
case TOGGLE_TODO: //改变事项状态
return state.map(todo=>{
if(todo.id === action.id){
return {
...todo,
completed:!todo.completed
}
}else{
return {
...todo
}
}
})
default :
return state
}
}
export default todos
3、store
通过createStore接受reducer创建store对象
store对象提供了dispatch来发送action,通过getState获取全局state,通过subscribe订阅state变化
import { addTodo, toggleTodo, setFilter, setTodoText } from '../actions/index';
import { createStore } from 'redux'
import rootReducer from '../reducers/index'
const store = createStore(rootReducer)
//打印state
console.log(store.getState())
//订阅变化
const subscribeInfo = store.subscribe(()=>{
console.log(store.getState())
})
//触发actions
store.dispatch(addTodo("添加第一项待办事件"))
store.dispatch(toggleTodo(0))
store.dispatch(setFilter('active'))
store.dispatch(setTodoText('添加todolist文字描述'))
//订阅
subscribeInfo()
三、react-redux集成(链接redux-state和redux-component)
react-redux库提供的功能:
1、向根组件注入Store=>Provider组件
2、链接React组件和Redux状态成=>connect
3、获取React组件所需的State和Actions=>map api
使用了redux的引入将组件分为:展示型组件(redux)和容器型组件(redux),具体区别:
重点:容器型组件读写操作均是通过redux store中进行 以及对state数据的规划
具体实现步骤:
1、创建container存储容器型组件
编写容器型组件:通过redux的connect方法,将state、action映射到组建的props中
import {connect} from 'react-redux'
import { toggleTodo } from '../actions/index' //引入action
import TodoList from '../components/TodoList'
const mapStateToProps=(state)=>({ //将redux的state映射到react的props
todos:getVisableTodos(state.todos,state.filter), //react组件需要的todos从redux获得
})
const mapDispatcThoProps=(distapch)=>({ //将redux的action映射到react的props
toggleTodo: id=>distapch(toggleTodo(id))
})
const getVisableTodos = (todos,filter)=>{
switch(filter){
case 'all':
return todos
case 'completed':
return todos.map(todo=>todo.completed)
case 'active':
return todos.map(todo=>!todo.completed)
default:
return new Error('Unkn')
}
}
//connect接受两个函数参数
export default connect(mapStateToProps,mapDispatcThoProps)(TodoList)
app.js 测试页面。
import React, { Component } from 'react'
import AddTodoContainer from '../../containers/AddTodoContainer'
import FooterContainer from '../../containers/FooterContainer'
import TodoListContainer from '../../containers/TodoListContainer'
export default class App extends Component {
render() {
return (
<div>
<AddTodoContainer></AddTodoContainer>
<FooterContainer></FooterContainer>
<TodoListContainer></TodoListContainer>
</div>
)
}
}
todolist组件
import React, { Component } from 'react'
import Todo from './Todo'
export default class TodoList extends Component {
render() {
const { todos } = this.props
return (
<ul>
{
todos.map(todo=>{
return <Todo key={todo.id} {...todo}></Todo>
})
}
</ul>
)
}
}
action
import {ADD_TODO, TOGGLE_TODO, SET_FILTER, SET_TODO_TEXT} from "./actionTypes"
let nextTodoId = 0
//新增待办事项
export const addTodo = (text, type, id)=>({
id: nextTodoId++,
type: ADD_TODO,
text
})
//更改待办事项状态
export const toggleTodo = id=>({
type: TOGGLE_TODO,
id
})
//设置过滤状态
export const setFilter = filter=>({
type: SET_FILTER,
filter
})
//设置事项文字
export const setTodoText = text=>({
type: SET_TODO_TEXT,
text
})
reducers改变redux
import {ADD_TODO, TOGGLE_TODO} from "../actions/actionTypes"
const todos = (state = [],action)=>{ //此处的state非原始initalState,传递进来的是todos
switch(action.type){
case ADD_TODO: //添加待办事项
return [
...state,
{
text: action.text,
completed: false
}
]
case TOGGLE_TODO: //改变事项状态
return state.map(todo=>{
if(todo.id === action.id){
return {
...todo,
completed:!todo.completed
}
}else{
return {
...todo
}
}
})
default :
return state
}
}
export default todos
注入store
通过redux的createStore以reducers为参数创建store
通过react-redux的provider组件将store传入项目。
部分代码示例
import { createStore } from 'redux' // 引入createStore函数
import { Provider } from 'react-redux' //引入传递store组件
import rootReducer from './reducers/index' // 引入reducer
const store = createStore(rootReducer) //创建store
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
四、异步action
异步action关键时间点,发送阶段、成功/失败阶段
处理异步action需要redux-thunk的帮助
步骤1、定义异步action
//异步请求
const fetchTodosRequest = ()=>({
type: FETCH_TODO_REQUEST
})
const fetchTodosSuccess = (data)=>({
type: FETCH_TODO_SUCCESS,
data
})
const fetchTodosFailure = (err)=>({
type: FETCH_TODO_FAILURE,
err
})
export const fetchTodos = ()=>{
return (dispatch)=>{
dispatch(fetchTodosRequest())
return fetch('./mock/todos.json').then(res=>{
dispatch(fetchTodosSuccess(res))
},err=>{
dispatch(fetchTodosFailure(err))
})
}
}
顺便定一下mock数据
[{
"id":100,
"text":"学习react100",
"completed":false
},{
"id":101,
"text":"学习react101",
"completed":false
}]
2、添加reducer部分,使其处理异步actiontype。
为了处理异步情况,添加state新变量来描述
import {ADD_TODO, TOGGLE_TODO,
FETCH_TODO_REQUEST,
FETCH_TODO_SUCCESS,
FETCH_TODO_FAILURE
} from "../actions/actionTypes"
const syncInitState = {
isFetching: false,
error: null,
data: []
}
const reducer = (state=syncInitState,action)=>{
switch(action.tyoe){
case FETCH_TODO_REQUEST:
return {
...state,
isFetching: true,
}
case FETCH_TODO_SUCCESS:
return {
...state,
isFetching: false,
data: action.data
}
case FETCH_TODO_FAILURE:
return {
...state,
isFetching: false,
data: action.err
}
default:
return {
...state,
data: todos(state.data,action)
}
}
}
const todos = (state = [],action)=>{ //此处的state非原始initalState,传递进来的是todos
switch(action.type){
case ADD_TODO: //添加待办事项
return [
...state,
{
id: action.id,
text: action.text,
completed: false
}
]
case TOGGLE_TODO: //改变事项状态
return state.map(todo=>{
if(todo.id === action.id){
return {
...todo,
completed:!todo.completed
}
}else{
return {
...todo
}
}
})
default :
return state
}
}
export default reducer
3、因为state发生了变化,所以容器型组件也要做出相应改变,原来state.todos数据变味了state.todos.data,并添加异步事件 fetchTodos: ()=>distapch(fetchTodos())
import {connect} from 'react-redux'
import { toggleTodo } from '../actions/index' //引入action
import TodoList from '../components/TodoList'
const mapStateToProps=(state)=>({ //将redux的state映射到react的props
todos:getVisableTodos(state.todos.data,state.filter), //react组件需要的todos从redux获得
})
const mapDispatcThoProps=(distapch)=>({ //将redux的action映射到react的props
toggleTodo: id=>distapch(toggleTodo(id)) ,
fetchTodos: ()=>distapch(fetchTodos())
})
const getVisableTodos = (todos,filter)=>{
switch(filter){
case 'all':
return todos
case 'completed':
return todos.map(todo=>todo.completed)
case 'active':
return todos.map(todo=>!todo.completed)
default:
return new Error('Unkn')
}
}
//connect接受两个函数参数
export default connect(mapStateToProps,mapDispatcThoProps)(TodoList)
4、引入redux-thunk的thunkMiddleware 在创建store时通过redux的applyMiddleware使用,部分代码
import { createStore, applyMiddleware } from 'redux' // 引入createStore函数
import thunkMiddle from 'redux-thunk'
const store = createStore(rootReducer,applyMiddleware(thunkMiddle)) //创建store
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/redux/App';
import reportWebVitals from './reportWebVitals';
// import store from './store/index'
import { createStore, applyMiddleware } from 'redux' // 引入createStore函数
import { Provider } from 'react-redux' //引入传递store组件
import rootReducer from './reducers/index' // 引入reducer
import thunkMiddle from 'redux-thunk'
const store = createStore(rootReducer,applyMiddleware(thunkMiddle)) //创建store
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
补充:
1、vscode react相关快捷键:
rcc=>快速创建react类
2、selector函数
添加selector函数,实现state和视图层的解耦