Redux(简单版本)
1.安装redux
npm i redux
2.配置store
// 引入createStore
// store.js
import { createStore } from "redux";
import countReducer from "./count_reducer";
export default createStore(countReducer);
3.使用combineReducers 来组合多个reducer
import { createStore, applyMiddleware, combineReducers } from "redux";
import CountReducer from "./reducers/count";
import UserReducer from "./reducers/user";
import thunk from "redux-thunk";
export default createStore(
combineReducers({
count: CountReducer,
user: UserReducer,
}),
applyMiddleware(thunk)
);
4.配置reducer
// count_reducer.js
const countReducer = (prevState = 0, action) => {
const { type, data } = action;
switch (type) {
case "increment":
return prevState + data;
case "decrement":
return prevState - data;
default:
return prevState;
}
};
export default countReducer;
5.组件中使用
store.getState()获取数据
// Count.jsx
import store from "../../redux/store";
const [count,setCount] = useState(store.getState())
<div>当前求和为: {count}</div>
store.dispatch()
const increment = ()=>{
store.dispatch({type:'increment',data:'xxx'})
}
//这样调用后count 并不会引起变化,此时需要store的另一个api
store.subscribe()
//在类式组件中:这个api 需要在componentDidMount 钩子中调用,并且需要主动调用this.setState()来主动刷新页面重新渲染。
componentDidMount(){
store.subscribe(()=>{
this.setState({}) // 传入空对象,目的是让页面再次刷新下
})
}
// 函数式组件中:
const [count,setCount] = useState(store.getState())
useEffect(()=>{
store.subscribe(()=>{
setCount(store.getState()) // 此处目的同样是重新渲染页面
})
},[])
redux (完善版本)
在简单版本的基础上完善并规范化一些操作。
1.创建常量文件,统一规范
//新建 constant.js
export const INCREMENT = "increment";
export const DECREMENT = "decrement";
在action.js 和 reducer.js 中都引入 常量。
2.规范化操作action
// 之前都是在组件中调用dispatch
const increment = ()=>{
store.dispatch({type:'increment',data:'xxx'})
},
这样每次都得来写action。现在把action统一规范到文件中。
新建 count_action.js
import { INCREMENT, DECREMENT } from "./constant";
export const createIncrementAction = (data) => ({
type: INCREMENT,
data,
});
export const createDecrementAction = (data) => ({
type: DECREMENT,
data,
});
组件中引入
import {
createIncrementAction,
createDecrementAction,
} from "../../redux/count_action";
// 直接使用
const increment = () => {
store.dispatch(createIncrementAction(selectValue));
};
const decrement = () => {
store.dispatch(createDecrementAction(selectValue));
};
异步action 和同步action
根据action的类型来进行判断,若action 为一个 Object对象,则为同步。
若 action 为一个函数,则为异步。
使用 redux-thunk中间件来处理异步action
npm i redux-thunk
// store.js
// 引入createStore
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk"; // 引入thunk
import countReducer from "./count_reducer";
export default createStore(countReducer, applyMiddleware(thunk));
// count_action.js
import { INCREMENT, DECREMENT } from "./constant";
export const createIncrementAction = (data) => ({
type: INCREMENT,
data,
});
export const createDecrementAction = (data) => ({
type: DECREMENT,
data,
});
//此处就是异步action 返回一个函数,在返回的函数中写异步操作就行
export const createIncrementAsync = (data, time) => {
return (dispatch) => {
setTimeout(() => {
dispatch(createIncrementAction(data));
}, time);
};
};
react-redux
react官方出的一个操作redux的库,更加方便舒服的在react项目中使用redux
react-redux的基本使用流程
// 创建容器层 container/count/index.js
// 使用 connnect 来连接UI层和容器层
// ui 组件
import CountUi from "../../components/Count/index";
import { increment } from "../../redux/count_actions";
//使用connect 将 ui组件和 容器组件连接
import { connect } from "react-redux";
const mapStateToProps = (state) => {
return {
count: state,
};
};
const mapDiapatchToProps = (dispatch) => {
return {
jia: (number) => dispatch(increment(number)),
};
};
// !!! 注意 connect 方法的使用
export default connect(mapStateToProps, mapDiapatchToProps)(CountUi);
// 将原来引入<Count /> 组件的地方换成 容器组件
// 将 store 以props 的形式传入
import React from "react";
import Count from "./containers/count";
import store from "./redux/store";
function App() {
return (
<>
<Count store={store} />
</>
);
}
export default App;
// count UI 组件中 使用 props 的形式来使用
function Count(props) {
console.log("props", props);
const mincrement = () => {
props.jia(100);
};
return (
<>
<div>Count is {props.count}</div>
<button onClick={mincrement}>+</button>
</>
);
}
react-redux 的优化
优化一:不必将容器层和ui层分开写了,写到一个文件即可
// components/count/index
import React, { useEffect, useState } from "react";
import { connect } from "react-redux";
import {
increment,
decrement,
asyncIncrement,
} from "../../redux/count_actions";
function Count(props) {
console.log("props", props);
const mincrement = () => {
props.jia(100);
};
const mdecrement = () => {
props.jian(20);
};
const masyncIncrement = () => {
props.asyncJia(30, 1000);
};
return (
<>
<div>Count is {props.count}</div>
<button onClick={mincrement}>+</button>
<button onClick={mdecrement}>-</button>
<button onClick={masyncIncrement}>async+</button>
</>
);
}
export default connect(
(state) => ({ count: state }),
// (dispatch) => ({
// jia: (number) => dispatch(increment(number)),
// jian: (number) => dispatch(decrement(number)),
// asyncJia: (num, time) => dispatch(asyncIncrement(num, time)),
// })
//简写优化: react-redux 自动帮我们进行了dispatch 分发动作。
{
jia: increment,
jian: decrement,
asyncJia: asyncIncrement,
}
)(Count);
优化二: 使用react-redux 提供的Provider ,不用给每个组件都用props来传递store
//原来的是这样: App.jsx
import store from "./redux/store";
function App() {
return (
<>
<Count store={store} />
{/* 这里有多少个组件都得传递store */}
{/* <XXX store={store} /> */}
{/* <XXX store={store} /> */}
{/* <XXX store={store} /> */}
</>
);
}
// 优化后: main.jsx
import store from "./redux/store";
import { Provider } from "react-redux";
ReactDOM.createRoot(document.getElementById("root")).render(
<Provider store={store}>
<App />
</Provider>
);
使用useSelector和useDispatch 替换connect
import { useSelector, useDispatch, connect } from "react-redux";
//原来是使用connect 来 连接ui和数据层,写起来比较麻烦。
// 数据和方法都在props中。
function Page1 (props:any){
const clickbtn = ()=>{
props.increment(1)
// props.decrement(2);
// props.asyncIncrement(2, 1000);
}
return (
<>
<div>{props.info.name}</div>
<button onClick={clickbtn}>+ - </button>
</>
)
}
export default connect((state) => ({ info: (state as any).info }), {
increment: incrementAction,
decrement: decrementAction,
asyncIncrement: asyncIncrementAction,
})(Page1);
// 现在使用useSelector和useDispatch替换connect
function Page1 (){
const info = useSelector(state=>state.info)
const dispatch = useDispatch()
const clickbtn = ()=>{
// dispatch(incrementAction(1))
// dispatch(decrementAction(2))
dispatch(asyncIncrementAction(2, 1000) as any)
}
return (
<>
<div>{info.name}</div>
<button onClick={clickbtn}>+ - </button>
</>
)
}