问题描述:下面是一个全选按钮组件Unlimited,可以传入一个点击事件,一个checked选中状态.
- 在组件中我使用memo避免在父组件中渲染时重复渲染
- memo有第二参数,返回true则不会渲染,想着只需要checked的状态来判断。默认浅比较prop,函数onChange都不一样都会重建导致渲染
export const Unlimited: FC<{ onChange: () => void; checked: boolean }> = memo(
({ onChange, checked }) => {
return (
<span
className={styles.all}
onClick={onChange}
style={{ borderColor: checked ? '' : 'transparent' }}
>
不限
</span>
)
},
// (pre, next) => pre.checked === next.checked,
)
组件描述:使用场景

1.页面首次加载,会有些参数从前页面跳转过来,回显这些条件。比如价格,酒店等级。这些条件又会以tag的形式显示在下面。
- 当点击上面组件:不限 将取消选中,且删除对应的tag
- 这里使用context 传递 组合好的newTages 数组,将会在价格,酒店组件中使用到,用于点击“不限”按钮,删除对应tag
tags组件用来回显选中的条件
const Tags = () => {
const { tags, priceRange, star, confirmType } = useBarState()
const dispatch = useDispatchBarState()
const list = tags ?? []
// 首次加载首页会有部分筛选条件回显
useEffect(() => {
const params: HotelParams = {
priceRange,
star,
confirmType,
}
const newTages = Object.values(params).reduce((total, next) => {
return total.concat(next ?? [])
}, [])
if (!newTages.length) return
dispatch && dispatch({ tags: newTages })
}, [])
.....省略代码
}
问题点: 这里可以发现,tags组件在选中组件下面,通过context 传递过去的数组 是在 价格,酒店组件初始化渲染之后。
这会导致第一步中的Unlimited全选组件onChange时间中使用的newTages 永远是undefined
来看价格组件代码:
export const PriceRange: FC = () => {
const { priceRange, tags } = useBarState()
const newTages = tags ?? []
console.log(newTages) 外部在tags组件dispatch后是可以拿到最新的newTages
console.log(123, newTages)
const handleAll = () => {
console.log(234, newTages) 点击全选这里可拿不到最新的newTages
if (!(priceRange && priceRange.length)) return
const tagsList= deleteAll(newTages, 'price')
// dispatch && dispatch({ priceRange: [], tags: tagsList})
}
return useMemo(() => {
return <>
<Unlimited
onChange={handleAll}
checked={!(priceRange && priceRange.length)}
/>
<Radio.Group
options={PRICE_RANGE}
onChange={handleChange}
value={defaultRadio}
/>
</>
}, [priceRange, tagsList,...])
}
看实际打印情况:首次渲染。外部123都拿到了最新的newTages

点击不限按钮看看。哦吼,234 啥也没有

解决: 问题就出在全选组件Unlimited的使用memo后第二参数上,删掉就好了。
由于只使用checked来控制组件的渲染,首次渲染的onChange函数拿到的newTages就是空,加上useCallBack包裹onChange,写上依赖newTags也没用
知道原因后,其实还有别的解决办法,由于初始化页面会组合前一个页面带来的参数。如果把Tags 组件中生成newTages的逻辑放到整个页面渲染之前也是可以行的
function HotelListPage() {
const {
location: { state },
} = useHistory()
const paramsState = useInitParams(true, state ?? {})
// 在没有合并参数之前不渲染
if (!paramsState.isHotel) {
return null
}
return (
<Main />
)
}
把生成newTages的逻辑放到useInitParams中
// 初始化context
export const useInitParams = (isHotel: boolean = true, state?: any) => {
const paramsState = useBarState()
const dispatch = useDispatchBarState()
const { priceRange, star, confirmType } = state ?? {}
let tags = {}
if (priceRange || star || confirmType) {
tags = Object.values({ priceRange, star, confirmType }).reduce(
(total, next) => {
return total.concat(next ?? [])
},
/ [],
)
}
useEffect(() => {
dispatch &&
dispatch({
...defaultValue,
...(isHotel ? hotelDefault : ticketDefault),
...(state ?? {}),
// ...(tags && { tags }),
})
}, [])
return paramsState
}
疑问: 组件大量使用Unlimited这样的memo第二参数到底好不好?
网友评论