redux是react全家桶的一员,它为react给i共可预测化的状态管理机制。redux是将整个应用状态存储到一个地方,成为store,里面存放着一颗树状态(state,tree),组件可以派发dispatch行为action给store,而不是直接通知其他组件,其他组件可以通过订阅store中的状态state来刷新自己的视图。
redux是一种状态管理库,用于管理React应用中的全局状态,核心思想是将应用的状态集中存储在一个全局的Store中,使得状态的变化可追溯,可控制,可预测。
图片不够清除太专业,看下方老王去借书
component --> dispatch(action) --> reducer --> subscribe --> getState --> component
对象,描述要做的事情,项目中的每一个都是一个action
语法:{type:"命令",payload:"载荷"}
函数,用来处理action并更新状态,是Redux状态更新的地方
语法:函数签名:'(prevState,action)=>newState'
const reducer = (state, action) => {
switch(action.type){
case 'ADD':
state['sum'] = action.data
return [...state]
break
...
}
}
仓库,redux的核心,整合action和reducer
import {createStore} from 'redux'
let store = createStore(reducer)
在react-redux中,有两个核心概念,即Provider和connect。provider是一个React组件,用于将Redux的Store传递给React应用中的所有组件,从而是的组件可以访问到全局的状态。connect是一个高阶函数,用于将React组件连接到Redux的store,从而实现组件与Redux
store之间的数据传递和状态管理。
使用connect函数可以将React组件连接到Redux的store。通过在组件定义时调用connect函数,并传入需要的参数和回调函数,可以将组件与Redux的store进行连接,连接后,组件可以通过prop访问到Redux
Store中的状态,并且可以向Redux store派发action 来修改全局状态。
react-redux提供了一些高阶组件和hooks,可以帮助简化组件与Redux store之间的交互代码,例如,mapStateToProps和mapDispatchToProps参数可以帮助组件定义如何从redux store中获取状态和派发action的方式,从而减少了在组件中处理Redux store的繁琐代码,此外,React-redux还提供了一些hooks,例如useSelector和useDispatch,可以在函数组件中更方便的访问Redux store的状态和派发action。
react-redux购物车案例
效果:
目录结构:
action/index.js代码
import { ADD_PRODUCT, REMOVE_PRODUCT } from "../constants";
export const addProduct = id => ({
type: ADD_PRODUCT,
payload: id
});
export const removeProduct = id => ({
type: REMOVE_PRODUCT,
payload: id
});
cart/cartInte.js代码
import React from "react";
import PropTypes from "prop-types";
const CartItem = ({ name, price, quantity, itemTotal, removeProduct }) => (
<div className="cartWrapper">
<div>{name}</div>
<div>${price}</div>
<div>x{quantity}</div>
<div>= ${itemTotal}</div>
<div>
<button onClick={() => removeProduct(name)}>Remove</button>
</div>
</div>
);
CartItem.propTypes = {
name: PropTypes.string.isRequired,
price: PropTypes.string.isRequired,
quantity: PropTypes.number.isRequired,
itemTotal: PropTypes.string.isRequired,
removeProduct: PropTypes.func.isRequired
};
export default CartItem;
cart/index.js代码
import React from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import "./Cart.css";
import CartItem from "./CartItem";
import { getCart, getCartTotal } from "../../reducers";
import { removeProduct } from "../../actions";
const Cart = ({ cart, cartTotal, removeProduct }) => (
<React.Fragment>
<h2>Checkout Cart</h2>
{cart.map(({ name, price, quantity, itemTotal }) => (
<CartItem
key={name}
name={name}
price={price}
quantity={quantity}
itemTotal={itemTotal}
removeProduct={removeProduct}
/>
))}
<h2>Total</h2>
<div className="total">${cartTotal}</div>
</React.Fragment>
);
Cart.propTypes = {
cart: PropTypes.arrayOf(
PropTypes.shape({
name: PropTypes.string,
price: PropTypes.string,
quantity: PropTypes.number,
itemTotal: PropTypes.string
})
),
cartTotal: PropTypes.string,
removeProduct: PropTypes.func.isRequired
};
const mapStateToProps = state => ({
cart: getCart(state),
cartTotal: getCartTotal(state)
});
export default connect(mapStateToProps, { removeProduct })(Cart);
cart/Cart.css代码
.cartWrapper {
display: grid;
grid-template-columns: 150px 150px 100px 100px 200px;
grid-gap: 10px;
white-space: nowrap;
}
.total {
text-decoration: underline;
}
ProductList/index.js代码
import React from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import "./ProductList.css";
import Product from "./Product";
import { getProducts } from "../../reducers";
import { addProduct } from "../../actions";
const ProductList = ({ products, addProduct }) => (
<React.Fragment>
<h2>Product List</h2>
{products.map(({ name, price }) => (
<Product key={name} name={name} price={price} addProduct={addProduct} />
))}
</React.Fragment>
);
// propTypes验证,在给react组件传属性的的时候,定义属性的类型
ProductList.propTypes = {
products: PropTypes.arrayOf(
PropTypes.shape({
name: PropTypes.string.isRequired,
price: PropTypes.string.isRequired
})
),
addProduct: PropTypes.func.isRequired
};
const mapStateToProps = state => {
return { products: getProducts(state) };
};
export default connect(mapStateToProps, { addProduct })(ProductList);
ProductList/product.js代码
import React from "react";
import PropTypes from "prop-types";
const Product = ({ name, price, addProduct }) => (
<div className="productWrapper">
<div>{name}</div>
<div>{price}</div>
<div>
<button onClick={() => addProduct(name)}>Add</button>
</div>
</div>
);
Product.propTypes = {
name: PropTypes.string.isRequired,
price: PropTypes.string.isRequired,
addProduct: PropTypes.func.isRequired
};
export default Product;
ProductList/product.css代码
.productWrapper {
display: grid;
grid-template-columns: 150px 150px 300px;
grid-gap: 10px;
}
reducers/cart.js代码
import { combineReducers } from "redux";
import { ADD_PRODUCT, REMOVE_PRODUCT } from "../constants";
//state = [id1, id2]
const initialCartAllIds = [];
// Reducer(状态处理函数)
const cartAllIds = (state = initialCartAllIds, action) => {
switch (action.type) {
case ADD_PRODUCT: {
const newItem = action.payload;
if (state.includes(newItem)) return state;
return [...state, action.payload];
}
case REMOVE_PRODUCT: {
const unwantedItem = action.payload;
return state.filter(item => item !== unwantedItem);
}
default:
return state;
}
};
// state={id: {quantity: productQuantity}}
const initialCartById = {};
const cartById = (state = initialCartById, action) => {
switch (action.type) {
case ADD_PRODUCT: {
const newItem = action.payload;
const newQuantity = state[newItem] ? state[newItem].quantity + 1 : 1;
return { ...state, [newItem]: { quantity: newQuantity } };
}
case REMOVE_PRODUCT: {
const unwantedItem = action.payload;
const newState = { ...state };
delete newState[unwantedItem];
return newState;
}
default:
return state;
}
};
export const cart = combineReducers({
cartAllIds,
cartById
});
export const getCart = (products, cart) => {
return cart.cartAllIds.map(productName => {
const name = productName;
const price = products.productById[productName].price;
const quantity = cart.cartById[productName].quantity;
const itemTotal = (price * quantity).toFixed(2);
return { name, price, quantity, itemTotal };
});
};
export const getCartTotal = (products, cart) => {
return cart.cartAllIds
.reduce((pre, cur) => {
const price = products.productById[cur].price;
const quantity = cart.cartById[cur].quantity;
return pre + price * quantity;
}, 0)
.toFixed(2);
};
reducers/Products.js代码
import { combineReducers } from "redux";
import productsData from "../data";
// product ID is the product name in this case
// state = {[id]:{name: productName, price: productPrice}}
const initialProductById = (function() {
const state = {};
productsData.forEach(
({ name, price }) => (state[name] = { name, price: price.toFixed(2) })
);
return state;
})();
const productById = (state = initialProductById, action) => {
switch (action.type) {
default:
return state;
}
};
// state = [id1, id2]
const initialProductAllIds = productsData.map(product => product.name);
const productAllIds = (state = initialProductAllIds, action) => {
switch (action.type) {
default:
return state;
}
};
export const products = combineReducers({
productById,
productAllIds
});
export const getProducts = products => {
return products.productAllIds.map(key => products.productById[key]);
};
reducers/index.js代码
import { combineReducers } from "redux";
import * as productReducer from "./products";
import * as cartReducer from "./cart";
const reducer = combineReducers({
products: productReducer.products,
cart: cartReducer.cart
});
export const getProducts = state => productReducer.getProducts(state.products);
export const getCart = state => cartReducer.getCart(state.products, state.cart);
export const getCartTotal = state =>
cartReducer.getCartTotal(state.products, state.cart);
export default reducer;
configureStore.js代码
import { createStore } from "redux";
import reducer from "./reducers";
import { loadState, saveState } from "./localStorage";
const persistedState = loadState();
const store = createStore(
reducer,
persistedState,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
store.subscribe(() => {
const cartValue = store.getState().cart;
saveState({ cart: cartValue });
});
export default store;
constants.js代码
export const ADD_PRODUCT = "ADD_PRODUCT";
export const REMOVE_PRODUCT = "REMOVE_PRODUCT";
数据源data.js代码
const products = [
{
name: "Sledgehammer",
price: 125.75
},
{
name: "Axe",
price: 190.5
},
{
name: "Bandsaw",
price: 562.13
},
{
name: "Chisel",
price: 12.9
},
{
name: "Hacksaw",
price: 18.45
}
];
export default products;
index.js中引入store
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import App from "./components/App";
import store from "./configureStore";
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
localStorage.js代码
export const loadState = () => {
try {
const valueJSON = localStorage.getItem("state");
return JSON.parse(valueJSON) || undefined;
} catch (error) {
return undefined;
}
};
export const saveState = value => {
const valueJSON = JSON.stringify(value);
localStorage.setItem("state", valueJSON);
};
效果:
新增删除
完结~