1.redux理解
是什么?: redux是专门做状态管理的独立第3方库, 不是react插件
作用?: 对应用中状态进行集中式的管理(写/读)
开发: 与react-redux, redux-thunk等插件配合使用2. redux相关API
redux中包含: createStore(), applyMiddleware(), combineReducers()
store对象: getState(), dispatch(), subscribe()
react-redux: <Provider>, connect()()3. redux核心概念(3个)
action:
默认是对象(同步action), {type: 'xxx', data: value}, 需要通过对应的actionCreator产生,
它的值也可以是函数(异步action), 需要引入redux-thunk才可以
reducer
根据老的state和指定的action, 返回一个新的state
不能修改老的state
store
redux最核心的管理对象
内部管理着: state和reducer
提供方法: getState(), dispatch(action), subscribe(listener)4. redux工作流程
redux使用记录
目标效果:
目录结构:创建redux文件夹便于模块化管理
// action-types.js 存储常量名 // action 对象的 type 常量名称模块 export const INCREMENT ='increment' export const DECREMENT ='decrement'
// reducer.js内(定义方法改变redux内state的值) import { INCREMENT, DECREMENT } from "./action-types"; export function counter (state = 0, action) { switch (action.type) { case INCREMENT: return state += action.data case DECREMENT: return state -= action.data default: return state } }
// index.js内 import React from "react"; import { createRoot } from 'react-dom/client'; import { configureStore } from '@reduxjs/toolkit' import { counter } from './redux/reducers' import App from "./components/app"; // 根据counter函数创建store对象 const store = configureStore({ reducer: counter }) console.log(store, 'store'); const root = createRoot(document.getElementById('root')); function render () { root.render( // 将store传给app让其与组件有关联 <App store={store} /> ); } render() // 监听store内容更新dom store.subscribe(() => { render() })
// app.jsx内 // 导入 React 模块 import React, { Component } from "react"; // 引入常量名称 import { INCREMENT, DECREMENT } from "../redux/action-types"; // 引入action内的所有方法 // 暴露并创建react类 export default class App extends Component { addCountFn = () => { // store.dispatch调用store内的action函数 this.props.store.dispatch({ type: INCREMENT, data: this.select.value * 1 }); }; delCountFn = () => { this.props.store.dispatch({ type: DECREMENT, data: this.select.value * 1 }); }; incrementOddFn = () => { const count = this.props.store.getState(); if (count % 2 === 1) { this.props.store.dispatch({ type: INCREMENT, data: this.select.value * 1, }); } }; incrementAsyncFn = () => { setTimeout(() => { this.props.store.dispatch({ type: INCREMENT, data: this.select.value * 1, }); }, 1000); }; render() { const count = this.props.store.getState(); return ( <div> <h2>click {count} times</h2> <select ref={(select) => (this.select = select)}> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button onClick={this.addCountFn}>+</button> <button onClick={this.delCountFn}>-</button> <button onClick={this.incrementOddFn}>increment if odd</button> <button onClick={this.incrementAsyncFn}>increment async</button> </div> ); } }
优化 (模块化store)
之后的编码结构 只要用到redux 就先创建这四个文件夹
actions-type.js、reducers.js内代码不变
// actions.js内 import { INCREMENT,DECREMENT } from "./action-types"; export const increment = (number) => ({ type: INCREMENT, data: number }) export const decrement = (number) => ({ type: DECREMENT, data: number })
// store.js内 import { configureStore } from '@reduxjs/toolkit' import { counter } from './reducers' // 根据counter函数创建store对象 const store = configureStore({ reducer: counter }) console.log(store, 'store'); export default store
// app.jsx import React, { Component } from "react"; // 引入常量名称 // import { INCREMENT, DECREMENT } from "../redux/action-types"; // 引入action内的所有方法 import * as actions from "../redux/actions"; // 暴露并创建react类 export default class App extends Component { addCountFn = () => { // store.dispatch调用store内的action函数 this.props.store.dispatch(actions.increment(this.select.value * 1)); }; delCountFn = () => { this.props.store.dispatch(actions.decrement(this.select.value * 1)); }; incrementOddFn = () => { const count = this.props.store.getState(); if (count % 2 === 1) { this.props.store.dispatch(actions.increment(this.select.value * 1)); } }; incrementAsyncFn = () => { setTimeout(() => { this.props.store.dispatch(actions.increment(this.select.value * 1)); }, 1000); }; render() { const count = this.props.store.getState(); return ( <div> <h2>click {count} times</h2> <select ref={(select) => (this.select = select)}> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button onClick={this.addCountFn}>+</button> <button onClick={this.delCountFn}>-</button> <button onClick={this.incrementOddFn}>increment if odd</button> <button onClick={this.incrementAsyncFn}>increment async</button> </div> ); } }
// index.js import React from "react"; import { createRoot } from 'react-dom/client'; import App from "./components/app"; import store from './redux/store' const root = createRoot(document.getElementById('root')); function render () { root.render( <App store={store} /> ); } render() // 监听store内容更新dom store.subscribe(() => { render() })
遇到的问题
问题一:
// index.js内 ...引入的就不复制了 function render () { createRoot(document.getElementById('root')).render( <App store={store} /> ); } render() // 监听store内容更新dom store.subscribe(() => { render() })
报错:Warning: You are calling ReactDOMClient.createRoot() on a container that has already been passed to createRoot() before. Instead, call root.render() on the existing root instead if you want to update it.
修改:
// index.js内 const root = createRoot(document.getElementById('root')); function render () { root.render( <App store={store} /> ); } render() // 监听store内容更新dom store.subscribe(() => { render() })
问题二:引入 createStore被划掉问题
// index.js内 // 官方推荐 下载@reduxjs/toolkit这个包 用configureStore替换createStore import { configureStore } from '@reduxjs/toolkit' import rootReducer from './reducers' const store = configureStore({ reducer: rootReducer })