react组件封装的常见问题(一)

项目里面经常需要我们封装组件来提高复用性和开发效率,有时我们不仅需要用到动态输入的数据,可能还需要更改这部分数据,做一些交互。

  • 现有一个需求:做一个支持多选的tab组

  • Radio tab1、tab2 切换时传入封装组件的data会变化
    两个tab,下面是MultSelect 组件

  • 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;

切换为tab2时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。
  1. 判断是否为空
  • 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>
  )
}

  1. 静态死数据
  • 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更新的方式比较好。
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值