From bd08ba8df2f22391af3e9cfe6ab089f86f78b8cf Mon Sep 17 00:00:00 2001 From: xuan Date: Wed, 19 Oct 2022 15:39:48 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=88=20perf(=E8=B4=AD=E7=89=A9=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2):=20=E9=80=89=E6=8B=A9=E5=AE=A2=E6=88=B7=E9=A1=B9?= =?UTF-8?q?=E5=92=8C=E5=95=86=E5=93=81=E9=A1=B9=E7=9A=84=E6=80=A7=E8=83=BD?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/counter/index.tsx | 1 + .../components/colorKindItem/index.tsx | 212 +++++++++--------- .../components/shoppingCartItem/index.tsx | 128 +++++------ 3 files changed, 167 insertions(+), 174 deletions(-) diff --git a/src/components/counter/index.tsx b/src/components/counter/index.tsx index eb53efb..574e0a3 100644 --- a/src/components/counter/index.tsx +++ b/src/components/counter/index.tsx @@ -15,6 +15,7 @@ type params = { disable?: boolean, //是否禁用 } export default ({minNum = 0, maxNum = 10000, step=1, digits = 0, defaultNum = 0, onChange, onBlue, onClickBtn, unit = '', disable = false}: params) => { + console.log('Rerender component: Counter'); const [value, setValue] = useState({count:defaultNum}) const onPlus = (event) => { diff --git a/src/pages/shopping/components/colorKindItem/index.tsx b/src/pages/shopping/components/colorKindItem/index.tsx index 2cb02d2..058993f 100644 --- a/src/pages/shopping/components/colorKindItem/index.tsx +++ b/src/pages/shopping/components/colorKindItem/index.tsx @@ -1,16 +1,16 @@ import { View, Text, Image } from '@tarojs/components' import MCheckbox from '@/components/checkbox' import Counter from '@/components/counter' -import { FC, memo, ReactNode, useCallback, useEffect, useState } from 'react' +import { FC, forwardRef, memo, ReactNode, useCallback, useEffect, useState } from 'react' import classnames from 'classnames' import styles from './index.module.scss' import { debounce } from '@/common/util' import { formatImgUrl, formatPriceDiv } from '@/common/format' import { EnumSaleMode } from '@/common/Enumerate' -import { useNeedMemoCallback } from '@/use/useCommon' import { selectList } from '../../config' -import { ShoppingDispatchType, useShoppingDispatch, useShoppingState } from '../../context' +import { Goods, ShoppingDispatchType, ShoppingStateContextValue, useShoppingDispatch, useShoppingState } from '../../context' type PropsType = { + state?: Goods purchaserId: number itemData: Record & object orderType: EnumSaleMode @@ -18,119 +18,109 @@ type PropsType = { // 注意:如果在表单控件内部使用 useWatch,由于是直接从Context中取值,memo也会直接失去作用。 -const ColorKindItem: FC = memo( - props => { - console.log('Rerender component: ColorKindItem') - const { purchaserId, itemData, orderType = EnumSaleMode.Bulk } = props - const { colorStore } = useShoppingState() - const dispatch = useShoppingDispatch() - // @ts-ignore - const { checked } = colorStore[purchaserId]['goodsKind']![itemData.id] || { checked: false } +let ColorKindItem: FC = props => { + console.log('Rerender component: ColorKindItem') + const { state: goods, purchaserId, itemData, orderType = EnumSaleMode.Bulk } = props + // const { colorStore } = useShoppingState() + const dispatch = useShoppingDispatch() + // console.log('checked==>', checked) + // const { checked } = colorStore[purchaserId]['goodsKind']![itemData.id] || { checked: false } - //格式化金额 - const formatPrice = (price: number) => { - return Number(formatPriceDiv(price)) + //格式化金额 + const formatPrice = (price: number) => { + return Number(formatPriceDiv(price)) + } + + //格式化数量 + const formatCount = itemData => { + return itemData.sale_mode == EnumSaleMode.Bulk ? itemData.roll : itemData.length / 100 + } + + //格式化单位 + const formatUnit = itemData => { + return itemData.sale_mode == EnumSaleMode.Bulk ? '条' : '米' + } + + const handleSelect = () => { + // console.log('handleSelect') + dispatch({ + type: ShoppingDispatchType.UPDATE_CHANGED_CHECKBOX, + data: { + purchaserId: purchaserId, + goodsKind: { [itemData.id]: { ...goods!, checked: true } }, + }, + }) + } + const handleClose = () => { + dispatch({ + type: ShoppingDispatchType.UPDATE_CHANGED_CHECKBOX, + data: { + purchaserId: purchaserId, + goodsKind: { [itemData.id]: { ...goods!, checked: false } }, + }, + }) + } + + const getInputValue = debounce(async (num, itemData) => { + if (itemData.sale_mode === EnumSaleMode.Bulk) { + itemData.roll = num + } else { + itemData.length = num } + }, 300) - //格式化数量 - const formatCount = itemData => { - return itemData.sale_mode == EnumSaleMode.Bulk ? itemData.roll : itemData.length / 100 - } - - //格式化单位 - const formatUnit = itemData => { - return itemData.sale_mode == EnumSaleMode.Bulk ? '条' : '米' - } - - const handleSelect = () => { - // console.log('handleSelect') - dispatch({ - type: ShoppingDispatchType.UPDATE_CHANGED_CHECKBOX, - data: { - purchaserId: purchaserId, - goodsKind: { [itemData.id]: { ...colorStore[purchaserId].goodsKind?.[itemData.id]!, checked: true } }, - }, - }) - // setChangedCheckbox({ - // purchaserId: purchaserId, - // goodsKind: { [itemData.id]: { ...colorStore[purchaserId].goodsKind?.[itemData.id]!, checked: true } }, - // }) - } - const handleClose = () => { - dispatch({ - type: ShoppingDispatchType.UPDATE_CHANGED_CHECKBOX, - data: { - purchaserId: purchaserId, - goodsKind: { [itemData.id]: { ...colorStore[purchaserId].goodsKind?.[itemData.id]!, checked: false } }, - }, - }) - // setChangedCheckbox({ - // purchaserId: purchaserId, - // goodsKind: { [itemData.id]: { ...colorStore[purchaserId].goodsKind?.[itemData.id]!, checked: false } }, - // }) - } - - const getInputValue = debounce(async (num, itemData) => { - if (itemData.sale_mode === EnumSaleMode.Bulk) { - itemData.roll = num - } else { - itemData.length = num - } - }, 300) - - return ( - - - + return ( + + + + + + + {itemData.product_code}# {itemData.product_name} + + + {itemData.product_color_code} + {itemData.product_color_name} - - - {itemData.product_code}# {itemData.product_name} - - - {itemData.product_color_code} - {itemData.product_color_name} - + + + ¥ {formatPrice(itemData.sale_price)}/kg + + getInputValue(e, itemData)} + defaultNum={formatCount(itemData)} + step={selectList[orderType].step} + digits={selectList[orderType].digits} + onClickBtn={e => getInputValue(e, itemData)} + unit={formatUnit(itemData)} + minNum={selectList[orderType].minNum} + maxNum={selectList[orderType].maxNum} + /> - - ¥ {formatPrice(itemData.sale_price)}/kg - - getInputValue(e, itemData)} - defaultNum={formatCount(itemData)} - step={selectList[orderType].step} - digits={selectList[orderType].digits} - onClickBtn={e => getInputValue(e, itemData)} - unit={formatUnit(itemData)} - minNum={selectList[orderType].minNum} - maxNum={selectList[orderType].maxNum} - /> - - - - // 1111 - ) - }, - (preProp, nextProp) => { - const stringifyPreProp = JSON.stringify(preProp.itemData) - const stringifyNextProp = JSON.stringify(nextProp.itemData) - // console.log('memo==>', preProp, nextProp) - let needMemoized = true - if (preProp.purchaserId !== nextProp.purchaserId) { - needMemoized = false - } - if (preProp.orderType !== nextProp.orderType) { - needMemoized = false - } - if (stringifyPreProp !== stringifyNextProp) { - needMemoized = false - } - return needMemoized + + + ) +} +// State 分割组件 思路就是把 context直接通过props的形式传给组件,这样的话就解决了context强制刷新memo的问题了 +const withStateSlice = (comp, slice) => { + const MemoComp = memo(comp) + const Wrapper = (props, ref) => { + const state = useShoppingState() + return + } + return memo(forwardRef(Wrapper)) +} + +ColorKindItem = withStateSlice( + ColorKindItem, + (state: ShoppingStateContextValue, props: PropsType) => { + return state.colorStore[props.purchaserId]['goodsKind']![props.itemData.id] }, ) + export default ColorKindItem diff --git a/src/pages/shopping/components/shoppingCartItem/index.tsx b/src/pages/shopping/components/shoppingCartItem/index.tsx index d0f489e..5510938 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, memo, useEffect, useMemo, useState, useTransition } from 'react' +import { FC, forwardRef, memo, useEffect, useMemo, useState, useTransition } from 'react' import styles from './index.module.scss' import classnames from 'classnames' import { formatMeterDiv } from '@/common/format' @@ -9,14 +9,29 @@ import Divider from '@/components/divider' import ColorKindItem from '../colorKindItem' import { EnumSaleMode } from '@/common/Enumerate' import { selectList } from '../../config' -import { Goods, ShoppingDispatchType, useShoppingDispatch, useShoppingState } from '../../context' +import { Goods, ShoppingDispatchType, ShoppingStateContextValue, useShoppingDispatch, useShoppingState } from '../../context' import IconFont from '@/components/iconfont/iconfont' import { isEmptyObject } from '@/common/common' import classNames from 'classnames' import LoadingCard from '@/components/loadingCard' -type PropsType = { - itemData?: ShoppingCartData +interface ButtonPropsType { + isActive: boolean + onClick?: Function + children?: React.ReactNode + customStyle?: React.CSSProperties +} +// 订单类型 +const SaleModeButton: FC = props => { + const { onClick, children, isActive = false, customStyle } = props + const handleClick = () => { + onClick?.() + } + return ( + + {children} + + ) } const DrawerButton = memo<{ isOpen: boolean }>(({ isOpen }) => { @@ -33,9 +48,17 @@ enum BackEndSaleModeListFieldMap { weight_cut_color_list = 2, } -export default memo(props => { - const { itemData } = props +type PropsType = { + itemData?: ShoppingCartData + state?: { + goodsKind?: Goods + currentCheckedPurchaserId?: number + } +} +let ShoppingCartItem: FC = props => { + const { itemData, state } = props + const { goodsKind, currentCheckedPurchaserId } = state! const dispatch = useShoppingDispatch() const [openDetail, setOpenDetail] = useState(false) @@ -55,55 +78,25 @@ export default memo(props => { dispatch({ type: ShoppingDispatchType.UPDATE_SELECTED_AMOUNT, data: 0 }) } - const { currentCheckedPurchaserId, colorStore } = useShoppingState() + // const { currentCheckedPurchaserId, colorStore } = useShoppingState() console.log('Rerender component: shoppingCartItem') - // const memoList = useMemo(() => { - // console.log('ext useMemo') - // const component = itemData?.[BackEndSaleModeListFieldMap[selected]].map(item => { - // dispatch({ - // type: ShoppingDispatchType.UPDATE_CHANGED_CHECKBOX, - // data: { - // purchaserId: itemData?.purchaser_id!, - // goodsKind: { - // [item?.id]: { - // id: item?.id, - // estimate_amount: item.estimate_amount, - // checked: false, - // product_code: item.product_code, - // product_color_code: item.product_color_code, - // sale_mode: item.sale_mode, - // count: selected === EnumSaleMode.Bulk ? item.roll : Number(formatMeterDiv(item.length)), - // }, - // }, - // }, - // }) - // return - // }) - // // 向 context 中初始化数据 - // const result = itemData?.[BackEndSaleModeListFieldMap[selected]].length !== 0 ? component : 暂无数据 - - // return result - // }, [itemData, selected]) - const handleClickLayout = () => { if (currentCheckedPurchaserId === itemData?.purchaser_id) { return } dispatch({ type: ShoppingDispatchType.UPDATE_SELECTED_AMOUNT, data: 0 }) - // setSelectedAmount(0) - // setCurrentCheckedSaleMode(selected) dispatch({ type: ShoppingDispatchType.UPDATE_CURRENT_CHECKED_SALEMODE, data: selected }) dispatch({ type: ShoppingDispatchType.UPDATE_CURRENT_CHECKED_PURCHASERID, data: itemData?.purchaser_id as number }) - // setCurrentCheckedPurchaserId(itemData?.purchaser_id as number) } // 统计已选面料 const materialChecked = useMemo(() => { - const targetGoodsKind = colorStore?.[itemData?.purchaser_id!]?.['goodsKind'] + const targetGoodsKind = goodsKind if (!targetGoodsKind || isEmptyObject(targetGoodsKind)) return 0 return new Set( + // @ts-ignore Object.values(targetGoodsKind)?.reduce((prev, item: Goods) => { if (item.checked && item.sale_mode === selected) { return [...prev, item.product_code] @@ -111,13 +104,14 @@ export default memo(props => { return prev }, []), ).size - }, [colorStore, currentCheckedPurchaserId, selected, itemData]) + }, [goodsKind, currentCheckedPurchaserId, selected, itemData]) // 统计已选颜色 const colorChecked = useMemo(() => { - const targetGoodsKind = colorStore?.[itemData?.purchaser_id!]?.['goodsKind'] + const targetGoodsKind = goodsKind if (!targetGoodsKind || isEmptyObject(targetGoodsKind)) return 0 return new Set( + // @ts-ignore Object.values(targetGoodsKind).reduce((prev, item: Goods) => { if (item.checked && item.sale_mode === selected) { return [...prev, item.product_color_code] @@ -125,21 +119,23 @@ export default memo(props => { return prev }, []), ).size - }, [colorStore, currentCheckedPurchaserId, selected, itemData]) + }, [goodsKind, currentCheckedPurchaserId, selected, itemData]) // 统计已选条数 / 米数 const lengthOrRollChecked = useMemo(() => { - const targetGoodsKind = colorStore?.[itemData?.purchaser_id!]?.['goodsKind'] + const targetGoodsKind = goodsKind if (!targetGoodsKind || isEmptyObject(targetGoodsKind)) return 0 return ( + // @ts-ignore Object.values(targetGoodsKind).reduce((prev, item: Goods) => { if (item.checked && item.sale_mode === selected) { + // @ts-ignore return prev + item.count } return prev }, 0) || 0 ) - }, [colorStore, currentCheckedPurchaserId, selected, itemData]) + }, [goodsKind, currentCheckedPurchaserId, selected, itemData]) const [isPending, startTransition] = useTransition() @@ -205,7 +201,6 @@ export default memo(props => { - {/* */} @@ -213,7 +208,7 @@ export default memo(props => { )} ) -}) +} interface GoodsListPropType { itemData?: ShoppingCartData @@ -271,22 +266,29 @@ const GoodsList = memo(props => { ) }) - -interface ButtonPropsType { - isActive: boolean - onClick?: Function - children?: React.ReactNode - customStyle?: React.CSSProperties -} -// 订单类型 -const SaleModeButton: FC = props => { - const { onClick, children, isActive = false, customStyle } = props - const handleClick = () => { - onClick?.() +// State 分割组件 思路就是把 context直接通过props的形式传给组件,这样的话就解决了context强制刷新memo的问题了 +const withStateSlice = (comp, slice) => { + const MemoComp = memo(comp, (prevProps, nextProps)=>{ + let needMemo = true + if (JSON.stringify(prevProps.itemData) !== JSON.stringify(nextProps.itemData)) { + needMemo = false + } + if(JSON.stringify(prevProps.state) !== JSON.stringify(nextProps.state)){ + needMemo = false + } + return needMemo + }) + const Wrapper = (props, ref) => { + const state = useShoppingState() + console.log('state===>', state) + return } - return ( - - {children} - - ) + return memo(forwardRef(Wrapper)) } + +ShoppingCartItem = withStateSlice(ShoppingCartItem, (state: ShoppingStateContextValue, props) => ({ + goodsKind: state.colorStore?.[props.itemData?.purchaser_id]?.['goodsKind']!, + currentCheckedPurchaserId: state.currentCheckedPurchaserId, +})) + +export default ShoppingCartItem