项目里面经常需要我们封装组件来提高复用性和开发效率,有时我们不仅需要用到动态输入的数据,可能还需要更改这部分数据,做一些交互。
-
现有一个需求:做一个支持多选的tab组
-
Radio tab1、tab2 切换时传入封装组件的data会变化
-
index.jsx 父组件
import React, { useState } from 'react'
import { Radio } from 'antd';
import MultSelect from './MultSelect';
const optionsWithDisabled = [
{
label: 'tab1',
value: 'tab1',
},
{
label: 'tab2',
value: 'tab2',
}
];
const data1 = [
{ id: '1', isActive: true, name: '我是tab1内容1', sub: '' },
{ id: '2', isActive: false, name: '我是tab1内容2', sub: '' },
{ id: '3', isActive: false, name: '我是tab1内容3', sub: '' },
];
const data2 = [
{ id: '1', isActive: true, name: '我是tab2内容1', sub: '' },
{ id: '2', isActive: false, name: '我是tab2内容2', sub: '' },
{ id: '3', isActive: false, name: '我是tab2内容3', sub: '' },
];
export default function MyComp() {
const [selectValue, setSelectValue] = useState('tab1');
const [data, setData] = useState(data1);
const onChange = ({ target: { value } }) => {
console.log('radio4 checked', value);
setSelectValue(value);
setData(value==='tab1'?data1:data2);
};
const handleDataChange=(value)=>{
console.log(value);
}
return (
<div>
<Radio.Group
options={optionsWithDisabled}
onChange={onChange}
value={selectValue}
optionType="button"
/>
<MultSelect data={data} handleDataChange={handleDataChange} />
</div>
)
}
错误的写法
- MultSelect .jsx
- data传进来作为dataList的默认值,实际上useState初始值只会在第一次render使用,之后更新都是在上一次setState更新后的基础上去更新,不再使用初始值了;如果传进来的data不是一个相对静态(immutable)的值,就不能作为useState的初始值使用,否则会违背它的设计模式。
import React, { useState, useEffect } from 'react';
import './MultSelect.scss';
const MultSelect = ({data,handleDataChange}) => {
const [idList, setIdList] = useState([]);
const [dataList, setDataList] = useState(data); //此处data只有初始值使用了一次,会导致切换Radio这个组件不更新data
const handleClick = (e, idx) => {
let dataTemp = dataList.map((item) => {
return {
...item,
isActive:
item.id == idx ? (item.isActive = !item.isActive) : item.isActive,
};
});
let idList = dataTemp
.filter((item) => item.isActive)
.map((item2) => item2.id);
setDataList(dataTemp);
setIdList(idList);
handleDataChange({
data: dataTemp,
idList,
});
};
return (
<div className="list-container">
{dataList.map((i) => {
return (
<span
key={i.id}
className={
i.isActive ? 'item slected ' : 'item'
}
onClick={(e) => {
handleClick(e, i.id);
}}
>
{i.name}
<sub>{i.sub}</sub>
</span>
);
})}
</div>
);
};
export default MultSelect;
正确的写法1
- MultSelect .jsx
- data使用useEffect监听,再用setState保存,这样才可以接收动态data,能够及时变化
import React, { useState, useEffect } from 'react';
import './MultSelect.scss';
const MultSelect = ({ data, handleDataChange }) => {
const [idList, setIdList] = useState([]);
const [list, setList] = useState(data);
const handleClick = (e, idx) => {
let pollutionTemp = list.map((item) => {
return {
...item,
isActive:
item.id == idx ? (item.isActive = !item.isActive) : item.isActive,
};
});
let idList = pollutionTemp
.filter((item) => item.isActive == true)
.map((item2) => item2.id);
setList(pollutionTemp);
setIdList(idList);
};
useEffect(() => {
if (list.length == 0 || idList == '') return;
handleDataChange({
data: list,
idList,
});
}, [idList, list]);
useEffect(() => {
//使用useEffect监听传入的data,再用setState更新
console.log('data',data);
setList(data);
let ids = [];
data.map((item) => {
if (item.isActive) ids.push(item.id);
});
setIdList(ids);
}, [data]);
return (
<div className="list-container">
{list.length > 0 &&
list.map((i) => {
return (
<span
key={i.id}
className={
i.isActive ? 'item slected ' : 'item'
}
onClick={(e) => {
handleClick(e, i.id);
}}
>
{i.name}
<sub>{i.sub}</sub>
</span>
);
})}
</div>
);
};
export default MultSelect;
错误写法的使用场景
- 如果传进来的data是immutable,一旦创建就不再改变了,那么就可以使用上述错误的写法。比如index.jsx文件中通过接口获取data(或者是静态死数据)保存后,就传给MultSelect组件,这样是可以的;但是需要注意,在index.jsx文件中引入组件时,应该需要判断data不为空,再去渲染组件,否则data还是会经历由空到有数据的转变,依旧会出问题;静态死数据也可以直接在index.jsx中保存data的state中,直接写为initialState(或者不妨直接传入组件),这样也不会发生由数据变化导致的组件不更新的bug。
- 判断是否为空
- index.jsx 父组件
import React, { useState } from 'react'
import { Radio } from 'antd';
import MultSelect from './MultSelect';
const optionsWithDisabled = [
{
label: 'tab1',
value: 'tab1',
},
{
label: 'tab2',
value: 'tab2',
}
];
export default function MyComp() {
const [selectValue, setSelectValue] = useState('tab1');
const [data, setData] = useState([]);
const onChange = ({ target: { value } }) => {
console.log('radio4 checked', value);
setSelectValue(value);
setData(value==='tab1'?data1:data2);
};
const handleDataChange=(value)=>{
console.log(value);
}
useEffect(() => {
//调用接口保存data数据
axios.get('http:111111.cn',{params:{p1:1}).then(res=>{
setData(res);
})
}, []);
return (
<div>
<Radio.Group
options={optionsWithDisabled}
onChange={onChange}
value={selectValue}
optionType="button"
/>
{data.length>0 && <MultSelect data={data} handleDataChange={handleDataChange} />}
</div>
)
}
- 静态死数据
- index.jsx 父组件
import React, { useState } from 'react'
import { Radio } from 'antd';
import MultSelect from './MultSelect';
const optionsWithDisabled = [
{
label: 'tab1',
value: 'tab1',
},
{
label: 'tab2',
value: 'tab2',
}
];
const data1 = [
{ id: '1', isActive: true, name: '我是tab1内容1', sub: '' },
{ id: '2', isActive: false, name: '我是tab1内容2', sub: '' },
{ id: '3', isActive: false, name: '我是tab1内容3', sub: '' },
];
export default function MyComp() {
const [selectValue, setSelectValue] = useState('tab1');
const [data, setData] = useState(data1);
const onChange = ({ target: { value } }) => {
console.log('radio4 checked', value);
setSelectValue(value);
setData(value==='tab1'?data1:data2);
};
const handleDataChange=(value)=>{
console.log(value);
}
return (
<div>
<Radio.Group
options={optionsWithDisabled}
onChange={onChange}
value={selectValue}
optionType="button"
/>
<MultSelect data={data} handleDataChange={handleDataChange} />
{/* <MultSelect data={data1} handleDataChange={handleDataChange} /> */}
</div>
)
}
- 这两种解法有很大的局限性,只能在特定的场合使用,在开发时还是尽量使用useEffect监听 + setState更新的方式比较好。