diff --git a/package.json b/package.json index 370d84d..33a3f04 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "@tarojs/taro-h5": "^3.5.5", "big.js": "^6.2.1", "dayjs": "^1.11.3", + "immer": "^9.0.16", "qs": "^6.10.3", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/project.private.config.json b/project.private.config.json index d1711c9..ce8d630 100644 --- a/project.private.config.json +++ b/project.private.config.json @@ -236,5 +236,5 @@ ] } }, - "libVersion": "2.26.2" + "libVersion": "2.27.3" } \ No newline at end of file diff --git a/src/components/InputX/index.tsx b/src/components/InputX/index.tsx index 481d8d1..1f41ea9 100644 --- a/src/components/InputX/index.tsx +++ b/src/components/InputX/index.tsx @@ -11,7 +11,6 @@ interface PropsType { } const InputX: FC = (props) => { const {customClassName, customStyle,customInputStyle, customInputClassName,...inputProps} = props - console.log('props', props); return ( diff --git a/src/pages/shopping/components/colorKindItem/index.tsx b/src/pages/shopping/components/colorKindItem/index.tsx index 5126c4b..1ef9643 100644 --- a/src/pages/shopping/components/colorKindItem/index.tsx +++ b/src/pages/shopping/components/colorKindItem/index.tsx @@ -10,21 +10,30 @@ import { EnumSaleMode } from '@/common/Enumerate' import { selectList } from '../../config' import { AdjestShoppingCartApi } from '@/api/shopping/index' import { Goods, ShoppingDispatchType, ShoppingStateContextValue, useShoppingDispatch, useShoppingState } from '../../context' -import { ShoppingStore } from '../../context/shoppingStore' +import { events, ShoppingStore } from '../../context/shoppingStore' import LabAndImg from '@/components/LabAndImg' +import { usePropsValue } from '@/use/useCommon' type PropsType = { state?: { - multipleSelection: Goods[] Observer: ShoppingStore } purchaserId: number itemData: Record & object + checked?: boolean + onChange?: (isChecked: boolean, goodsId: number) => void } const ColorKindItem: FC = props => { - const { state, purchaserId, itemData } = props - const dispatch = useShoppingDispatch() + console.log('rerender component ColorKindItem', props); + const { state, purchaserId, itemData, checked = false } = props + const [isChecked, setCheck] = usePropsValue({ + value: checked, + defaultValue: checked, + onChange: (value) => { + props.onChange?.(value, itemData.id) + } + }) //格式化数量 const formatCount = itemData => { @@ -32,6 +41,7 @@ const ColorKindItem: FC = props => { } useEffect(() => { + console.log('itemData==>',itemData); setCount(formatCount(itemData)) }, [itemData.roll, itemData.length]) @@ -46,7 +56,6 @@ const ColorKindItem: FC = props => { const handleCountChange = (nextValue: number) => { setCount(nextValue) } - //格式化单位 @@ -55,36 +64,21 @@ const ColorKindItem: FC = props => { } // 选中 const handleSelect = () => { - dispatch({ - type: ShoppingDispatchType.UPDATE_CHANGED_CHECKBOX, - data: { - purchaserId: purchaserId, - multipleSelection: { - ...state?.multipleSelection, - [itemData.id]: { - id: itemData?.id, - estimate_amount: itemData.estimate_amount, - product_code: itemData.product_code, - product_color_code: itemData.product_color_code, - sale_mode: itemData.sale_mode, - count: itemData.sale_mode === EnumSaleMode.Bulk ? itemData.roll : Number(formatMeterDiv(itemData.length)), - } - }, - }, - }) + const payload = { + id: itemData?.id, + estimate_amount: itemData.estimate_amount, + product_code: itemData.product_code, + product_color_code: itemData.product_color_code, + sale_mode: itemData.sale_mode, + count: itemData.sale_mode === EnumSaleMode.Bulk ? itemData.roll : Number(formatMeterDiv(itemData.length)), + } + events.trigger('updatePurchaserMultipleSelection', purchaserId, payload, 'add', itemData?.id) + setCheck(true) } // 未选中 const handleClose = () => { - const temp = {...state?.multipleSelection} - delete temp?.[itemData.id] - console.log('handleClose==>',temp); - dispatch({ - type: ShoppingDispatchType.UPDATE_CHANGED_CHECKBOX, - data: { - purchaserId: purchaserId, - multipleSelection: temp, - }, - }) + events.trigger('updatePurchaserMultipleSelection', purchaserId, null, 'delete', itemData.id) + setCheck(false) } const { fetchData } = AdjestShoppingCartApi() @@ -99,7 +93,7 @@ const ColorKindItem: FC = props => { itemData.roll = num targetColor.roll = num } else { - itemData.length = num + itemData.length = formatMeterMul(num) targetColor.length = formatMeterMul(num) } const res = await fetchData({ @@ -118,10 +112,10 @@ const ColorKindItem: FC = props => { return ( @@ -157,7 +151,26 @@ const ColorKindItem: FC = props => { // State 分割组件 思路就是把 context 直接通过 props 的形式传给组件,这样的话就解决了 context 强制刷新 memo 的问题了 // 那么当 context 内的 value 被更新的时候,react 只会强制渲染 Wrapper const withStateSlice = (comp, slice) => { - const MemoComp = memo(comp) + const MemoComp = memo(comp, (prevProps, nextProps) => { + let needMemo = true + if (prevProps.itemData !== nextProps.itemData) { + console.log('itemData 有变化'); + needMemo = false + } + if (prevProps.state.Observer !== nextProps.state.Observer) { + console.log('Observer 有变化'); + needMemo = false + } + if (prevProps.checked !== nextProps.checked) { + console.log('checked 有变化', prevProps.checked, nextProps.checked); + needMemo = false + } + if (prevProps.purchaserId !== nextProps.purchaserId) { + console.log('purchaserId 有变化'); + needMemo = false + } + return needMemo + }) const Wrapper = (props, ref) => { const state = useShoppingState() return @@ -167,7 +180,6 @@ const withStateSlice = (comp, slice) => { const ColorKindItemWithStateSlice = withStateSlice(ColorKindItem, (state: ShoppingStateContextValue, props: PropsType) => { return { - multipleSelection: state.colorStore[props.purchaserId]['multipleSelection'], Observer: state.Observer, } }) diff --git a/src/pages/shopping/components/shoppingCart/index.tsx b/src/pages/shopping/components/shoppingCart/index.tsx index 1ee10e1..07e97de 100644 --- a/src/pages/shopping/components/shoppingCart/index.tsx +++ b/src/pages/shopping/components/shoppingCart/index.tsx @@ -1,3 +1,4 @@ +import produce from 'immer' import { FC, ReactNode, useEffect, useMemo, useReducer, useRef } from 'react' import { GoodsMeta, @@ -7,6 +8,7 @@ import { ShoppingDispatchType, shoppingReducer, ShoppingStateContext, + throwError, } from '../../context' import { ColorStore, ShoppingStateContextValue } from '../../context' import { ShoppingStore } from '../../context/shoppingStore' diff --git a/src/pages/shopping/components/shoppingCartItem/index.tsx b/src/pages/shopping/components/shoppingCartItem/index.tsx index 883e1f9..5451899 100644 --- a/src/pages/shopping/components/shoppingCartItem/index.tsx +++ b/src/pages/shopping/components/shoppingCartItem/index.tsx @@ -1,5 +1,5 @@ import { Text, View } from '@tarojs/components' -import { FC, forwardRef, memo, useEffect, useMemo, useRef, useState, useTransition } from 'react' +import { FC, forwardRef, memo, useCallback, useEffect, useMemo, useRef, useState, useTransition } from 'react' import styles from './index.module.scss' import classnames from 'classnames' import { formatMeterDiv } from '@/common/format' @@ -15,8 +15,10 @@ import { alert, isEmptyObject } from '@/common/common' import classNames from 'classnames' import LoadingCard from '@/components/loadingCard' import { ShoppingCartListApi } from '@/api' -import { ShoppingStore } from '../../context/shoppingStore' +import { events, ShoppingStore } from '../../context/shoppingStore' import { usePropsValue } from '@/use/useCommon' +import produce, { setAutoFreeze } from 'immer' +import { nextTick } from '@tarojs/taro' interface ButtonPropsType { isActive: boolean @@ -24,6 +26,7 @@ interface ButtonPropsType { children?: React.ReactNode customStyle?: React.CSSProperties } +type OperationType = 'add' | 'delete' // 订单类型 const SaleModeButton: FC = props => { const { onClick, children, isActive = false, customStyle } = props @@ -62,7 +65,7 @@ type PropsType = { const ShoppingCartItem: FC = props => { const { state } = props - console.log('props ShoppingCartItem', props) + console.log('rerender component ShoppingCartItem', props) const [itemData, setItemData] = usePropsValue({ value: props.itemData, @@ -143,9 +146,45 @@ const ShoppingCartItem: FC = props => { const [isPending, startTransition] = useTransition() const { fetchData } = ShoppingCartListApi() - + // 更新当前客户的多选项 + const updatePurchaserMultipleSelection = (purchaserId, payload, operationType: OperationType, goodsId: number) => { + if(operationType === 'add'){ + dispatch({ + type: ShoppingDispatchType.UPDATE_CHANGED_CHECKBOX, + data: { + purchaserId: purchaserId, + multipleSelection: produce(multipleSelection, (multipleSelectionDraft) =>{ + if(!multipleSelectionDraft){ + return { + [goodsId]: payload + } + }else{ + multipleSelectionDraft[goodsId] = payload + } + }), + }, + }) + + }else if(operationType === 'delete'){ + const temp = {...multipleSelection} + delete temp?.[goodsId] + dispatch({ + type: ShoppingDispatchType.UPDATE_CHANGED_CHECKBOX, + data: { + purchaserId: purchaserId, + multipleSelection: temp, + }, + }) + } + } + // 发布订阅 + useEffect(()=>{ + events.on('updatePurchaserMultipleSelection', updatePurchaserMultipleSelection) + return () => { + events.off('updatePurchaserMultipleSelection', updatePurchaserMultipleSelection) + } + }, [updatePurchaserMultipleSelection]) // 发布订阅 - useEffect(() => { console.log('update multipleSelection',multipleSelection); const unsubscribe = state?.Observer?.subscribe(async id => { @@ -290,25 +329,60 @@ interface GoodsListPropType { } const GoodsList = memo(props => { const { itemData, selected, isPending, startTransition, multipleSelection } = props - + const prevMultipleSelection = useRef(multipleSelection) const currentSelected = useRef(null) const dispatch = useShoppingDispatch() const [component, setComponent] = useState(null) + // 使用 produce 更新特定的 ColorKindItem + const updateSpecifiedComponent = () => { + let newId + if(multipleSelection && prevMultipleSelection.current !== multipleSelection){ + for(let key in multipleSelection){ + if(!prevMultipleSelection.current?.hasOwnProperty(key)){ + newId = key + break + } + } + } + console.log('multipleSelection==+>',multipleSelection); + console.log('currentSelected', currentSelected.current, selected); + console.log('component',component); + if(component){ + if(itemData?.[BackEndSaleModeListFieldMap[selected]].length !== 0){ + setComponent(produce(component, (draft) => { + console.log('prev',component); + const index = (draft as unknown as any[]).findIndex((item)=> item.key === newId) + console.log('index',index) + if(index !== -1) { + const item = itemData?.[BackEndSaleModeListFieldMap[selected]].find(item => { + return item.id === Number(newId) + }) + console.log('item', item, newId); + draft![index] = + } + })) + }else{ + setComponent(暂无数据) + } + } + prevMultipleSelection.current = multipleSelection + } // 更新 GoodsList 组件 const updateComponent = () => { setComponent( - itemData?.[BackEndSaleModeListFieldMap[selected]].length !== 0 ? ( + () => itemData?.[BackEndSaleModeListFieldMap[selected]].length !== 0 ? ( itemData?.[BackEndSaleModeListFieldMap[selected]].map(item => { - console.log('item===>', item) - return + console.log('item===>', item, multipleSelection?.hasOwnProperty(item.id)) + return }) ) : ( 暂无数据 - ), + ) ) + prevMultipleSelection.current = multipleSelection } useEffect(() => { @@ -337,8 +411,8 @@ const GoodsList = memo(props => { }) updateComponent() } else { - // 重新把当前的选中状态赋值给ref 作为下一次比较的旧状态 currentSelected.current = selected + // 重新把当前的选中状态赋值给ref 作为下一次比较的旧状态 dispatch({ type: ShoppingDispatchType.UPDATE_CHANGED_CHECKBOX, data: { @@ -368,28 +442,32 @@ const GoodsList = memo(props => { const withStateSlice = (comp, slice) => { const MemoComp = memo(comp, (prevProps, nextProps) => { let needMemo = true - // const prevCheckedPurchaserId = prevProps.state.currentCheckedPurchaserId - // const nextCheckedPurchaserId = nextProps.state.currentCheckedPurchaserId - // const purchaser_id = prevProps.itemData.purchaser_id - // const prevMultipleSelection = prevProps.state.colorStore[purchaser_id].multipleSelection - // const nextMultipleSelection = nextProps.state.colorStore[purchaser_id].multipleSelection if (JSON.stringify(prevProps.itemData) !== JSON.stringify(nextProps.itemData)) { needMemo = false } - if(prevProps.itemData.purchaser_name === 'Hhjh'){ + if(prevProps.itemData.purchaser_name === 'JENNIE'){ console.log('------withStateSlice props-------'); console.log('withStateSlice props prevProps', prevProps); + console.log('withStateSlice props prevProps comparison itemData', prevProps.itemData === nextProps.itemData); console.log('withStateSlice props nextProps', nextProps); + console.log('withStateSlice props prevProps comparison multipleSelection', prevProps.state.multipleSelection === nextProps.state.multipleSelection); + console.log('withStateSlice props prevProps comparison currentCheckedPurchaserId', prevProps.state.currentCheckedPurchaserId === nextProps.state.currentCheckedPurchaserId); + console.log('withStateSlice props prevProps comparison Observer', prevProps.state.Observer === nextProps.state.Observer); + console.log('withStateSlice props prevProps comparison state', prevProps.state === nextProps.state); console.log('------withStateSlice props-------'); } - if (JSON.stringify(prevProps.state) !== JSON.stringify(nextProps.state)) { - console.log('MultipleSelection 有变化'); + if (prevProps.state.Observer !== nextProps.state.Observer) { + console.log('Observer 有变化'); + needMemo = false + } + if (prevProps.state.multipleSelection !== nextProps.state.multipleSelection) { + console.log('multipleSelection 有变化'); + needMemo = false + } + if (prevProps.state.currentCheckedPurchaserId !== nextProps.state.currentCheckedPurchaserId) { + console.log('currentCheckedPurchaserId 有变化'); needMemo = false } - // if (JSON.stringify(prevCheckedPurchaserId) !== JSON.stringify(nextCheckedPurchaserId)) { - // console.log('checkedPurchaserId 有变化'); - // needMemo = false - // } return needMemo }) const Wrapper = (props, ref) => { diff --git a/src/pages/shopping/context/index.ts b/src/pages/shopping/context/index.ts index d261ee3..64545e6 100644 --- a/src/pages/shopping/context/index.ts +++ b/src/pages/shopping/context/index.ts @@ -1,4 +1,5 @@ import { EnumSaleMode } from '@/common/Enumerate' +import produce from 'immer' import React, { Dispatch } from 'react' import { useContext } from 'react' import { ShoppingStore } from './shoppingStore' @@ -88,44 +89,54 @@ export type ShoppingAction = { data?: any } -export function shoppingReducer(state: ShoppingStateContextValue, action: ShoppingAction) { +export const shoppingReducer = produce((draft: ShoppingStateContextValue, action: ShoppingAction)=>{ const { type, data } = action switch (type) { case ShoppingDispatchType.UPDATE_MANAGE_STATUS: { - return { ...state, isManageStatus: data } + draft.isManageStatus = data + break } case ShoppingDispatchType.UPDATE_MULTIPLE_SELECTION_STATUS: { - return { ...state, isMultipleSelection: data } + draft.isMultipleSelection = data + break } case ShoppingDispatchType.UPDATE_CURRENT_CHECKED_PURCHASERID: { - return { ...state, currentCheckedPurchaserId: data } + draft.currentCheckedPurchaserId = data + break } case ShoppingDispatchType.UPDATE_CURRENT_CHECKED_SALEMODE: { - return { ...state, currentCheckedSaleMode: data } + draft.currentCheckedSaleMode = data + break } case ShoppingDispatchType.UPDATE_COLOR_STORE: { - return { ...state, colorStore: data } + draft.colorStore = data + break } case ShoppingDispatchType.UPDATE_SELECTED_AMOUNT: { - return { ...state, selectedAmount: data } + draft.selectedAmount = data + break } case ShoppingDispatchType.UPDATE_CHANGED_CHECKBOX: { - return { - ...state, - colorStore: { - ...state.colorStore, - [data.purchaserId as number]: { - purchaserId: data.purchaserId, - goodsKind: { ...state.colorStore[data.purchaserId]?.goodsKind, ...data.goodsKind }, - multipleSelection: data.multipleSelection ? data.multipleSelection : state.colorStore[data.purchaserId]?.multipleSelection, - }, - }, + if(!draft.colorStore[data.purchaserId]){ + draft.colorStore[data.purchaserId] = { + purchaserId: data.purchaserId, + goodsKind: data.goodsKind, + multipleSelection: data.multipleSelection + } + }else{ + draft.colorStore[data.purchaserId] = produce(draft.colorStore[data.purchaserId], purchaserDraft => { + purchaserDraft.purchaserId = data.purchaserId + purchaserDraft.goodsKind = { ...purchaserDraft.goodsKind, ...data.goodsKind } + purchaserDraft.multipleSelection = data.multipleSelection ? data.multipleSelection : purchaserDraft?.multipleSelection + }) } + break } default: throwError() } -} +}) + export const ShoppingStateContext = React.createContext(null) @@ -161,6 +172,6 @@ export interface InternalShoppingCartAction extends ShoppingCartAction { __INTERNAL__: React.MutableRefObject } -function throwError(): never { +export function throwError(): never { throw new Error('没有这个action.type') } diff --git a/src/pages/shopping/context/shoppingStore.ts b/src/pages/shopping/context/shoppingStore.ts index e93151a..bc20efe 100644 --- a/src/pages/shopping/context/shoppingStore.ts +++ b/src/pages/shopping/context/shoppingStore.ts @@ -1,7 +1,9 @@ // 用于优化数据流 结合发布订阅 更新组件内部状态可以组件自己处理 +import { Events } from "@tarojs/taro" import { ColorStore } from "." +export const events = new Events() export type SubscribeCallback = (changedGoods: any) => void diff --git a/yarn.lock b/yarn.lock index 2a31da9..509aac4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7558,6 +7558,11 @@ image-size@~0.5.0: resolved "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz" integrity sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ== +immer@^9.0.16: + version "9.0.16" + resolved "https://registry.npmmirror.com/immer/-/immer-9.0.16.tgz#8e7caab80118c2b54b37ad43e05758cdefad0198" + integrity sha512-qenGE7CstVm1NrHQbMh8YaSzTZTFNP3zPqr3YU0S0UY441j4bJTg4A2Hh5KAhwgaiU6ZZ1Ar6y/2f4TblnMReQ== + immutable@^4.0.0: version "4.1.0" resolved "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz"