React踩坑记录

hooks

useEffect() 在组件内调用,可以访问state的变量或者props, 执行时机是在DOM更新之后调用他

useEffect() 接受一个函数作为参数

useEffect的执行是异步的 不会阻塞代码

如何控制

useEffect( 
 () => { alert('someThing')   // 这是每次更新DOM后执行的代码
	return () => alert('clear up')  // 这是在执行前对上一个effct清除的代码
}, [type])   // 第二个参数是当这个state没有变化时,不执行

注意

当return 一个变量时, 在本回合( 最后 ) 执行代码

当return 一个函数时, 在下一次刷新之前执行

useEffect 和 async/await 不能嵌套使用

useEffect( async()=> {  // 错误
    订阅某状态
    return 卸载订阅
}, [] )

useEffect接收的第一个参数的返回值是在组件卸载时 调用的, 假如async, 则返回一个promise对象, 执行它当然会报错, 正确用法

const fn = async () => {
    fetch api...
}

useEffect(fn, [] )

子组件调用父组件方法

// 父组件
<Child handle={handle}>

// 子组件
 const handle = (props) => {
     props.handle()
 }

父组件调用子组件方法

// 子组件
import React, { forwardRef } from 'react'
const Child = () => {
 const son = () => {
       console.log('我是子组件方法')
    }
 return (<button onClick={son} />)
}
export default forwardRef(Child)


//父组件
import React, { useState, useRef, useEffect } from "react";
 // 使用useRef钩子
const parentRef = useRef()
// 创建变量接收
const [childrenEvent, setchildrenEvent] = useState()
// effect 监听parentRef
useEffect(()=>{
    setchildrenEvent(parentRef)
}, [parentRef])
 
return (
     <Child ref={parentRef} />
    )

react循环遍历数组用map函数

  • map函数返回一个新的数组,在map的回调函数里,迭代每一项的时候也必须有返回值。

  • forEach 没有返回值

在需要返回值的情况下 一定要用map!!!

在需要返回值的情况下 一定要用map!!!

在需要返回值的情况下 一定要用map!!!

react ANTD 中 Form 组件中自定义组件的取值问题

对于form 组件的Form.Item包裹其他自定义组件时, submit 会导致获取不到自定义组件的值

这时可以调用 setFieldsValue 方法, 将值赋给 表单

父组件
 // 定义回调函数
  const onPositionSelect = (value) => {
    // console.log(value)
    spaceListForm.setFieldsValue({
      parentId: value[value.length - 1]
    });
  };

<Form.Item
        label="选择上级空间点位"
        name="parentId"
        rules={[{ required: true, message: "请选择" }]}
      >
        <SelectPosition 
        onPositionSelect={onPositionSelect}
        options={spaceData} 
        placeholder='请选择上级节点' />
 </Form.Item>

子组件
// 接收父组件方法
  const onPositionSelect = props.onPositionSelect

  return (
     <Cascader
          options={options}
          fieldNames={{ label: 'node_name', value: 'id', children: 'Children' }}
          changeOnSelect
	 // 在onchange 的时候调用该方法
          onChange={onPositionSelect}
          placeholder={placeholder}
        />
  );

ANTD 中表单动态增减

  • Form 嵌套 Form.List 嵌套 Form.Item

  • Form.Item必须有 name={[name, 'type']}fieldKey={[fieldKey, 'type']} 属性 , 不然会控件重复, 选择一个其他都变

    <Form.Item
     name={[name, 'type']}
     label="设备类别"
    fieldKey={[fieldKey, 'type']}
    >
           <Select onChange={typeChange} allowClear>
              <Option value="1">电力</Option>
              <Option value="2">暖通</Option>
              <Option value="3">自动化</Option>
           </Select>
     </Form.Item>
    

React ANTD 中 表格 操作栏获取数据

const columns = [
    ....
    {
      title: "操作栏",
      render: (text) => <Button size="middle" onClick={()=>{cancelTask(text)}}>取消任务</Button>,
// text 是数据 οnclick={cancel()} 这样写会直接调用
// 只有用箭头函数 οnclick={()=>{cancel()}} 表示调用cancel函数
    }
   ]
   
   return (
   <Table columns={columns} dataSource={list}>
   )

ANTD中 表格 报错 唯一key值

  <Table
          columns={columns}
          dataSource={data}
          rowKey={(columns) => columns.id}
 />
加上rouKey字段, 接收一个函数, 默认参数是行数据, 指定唯一key值即可

React ANTD 中踩坑 抽屉报错 React does not recognize the switchScrollingEffect prop on a DOM element

从官网上粘下来的代码报错 (给DOM节点绑定了未知的属性)

原因: Drawer 挂载的 HTML 节点默认为body \

解决

<Drawer  getContainer={false} > // 让其挂载在当前dom上

ANTD 中 Tree 树形控件, 关于默认展开项defaultExpandAll不生效

因为tree组件是单向数据流, 也就是改变值不能生效(只在开始设置时有效), 而数据是异步请求,开始不存在

使用条件渲染

ANTD中抽屉组件 , 报错 React does not recognize the switchScrollingEffect prop on a DOM element ‘‘父组件有switch属性挂载到子组件DOM上’’

因为Drawer是默认挂载在body上, 设置 getContainer = {false} 让Drawer挂载在当前dom上即可解决问题

React异步加载

React.lazy

import React, { lazy, Suspense } from 'react';

const MyEcharts = lazy(() => import("../pages/Demo/compontent/MyEcharts"));

如果需要在异步时加一些动画 需要用到Suspense 组件

<React.Suspense fallback={<waitingComponent>}>
    <Component />
</React.Suspense>

React 动态路由

  • routes中定义路径
const VerData = lazy(() => import("../../components/verData/verData/verData.jsx"));
const VerDataInfo = lazy(() =>
  import("../../components/verData/verDataInfo/verDataInfo.jsx")
);

export const routes = [
      {
        path:'/verData',
        component:VerData
      },
      {
        path:'/verDataInfo/:id',
        component:VerDataInfo
      }
  ]
  • 导入路由历史对象
import { useHistory} from 'react-router-dom';
  // 获取路由对象
let history = useHistory();
  • 使用history.push跳转 传参使用query
 history.push({
      pathname: `equipmentInfo/${item.id}`,
      query: data,
    });

组件接收
this.props.location.query

React useEffect 偶尔出现警告 To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function

  • 问题原因 : 因为在进入组件时触发effect发请求, 请求没有完成就进入了其他组件发请求 , 导致重复请求

react 组件更新机制

Axios中 请求头设置 axios.defaults.headers.post[‘Content-Type’] 无效问题

描述 :给post请求设置content-type 为 form-data 请求中 还是json格式

解决: 使用qs插件给请求参数序列化一下

方法一
 在transformRequest里 转化
 axios.defaults.transformRequest = (data) => qs.stringify(data);

方法二
在拦截器里转化
axios.interceptors.request.use(
  (config) => { 
    config.data = qs.stringify(config.data)  // 转为formdata数据格式
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

react useEffect 忽略依赖项警告

 // eslint-disable-next-line react-hooks/exhaustive-deps

渲染优化

memo

<Father>

  <Button onClick={()=>{xxxx}}/>

  <Child />

</Father>

Qs : 当父组件点击事件触发,更新后 子组件也跟着更新

Rs: 使用memo 方法包裹子组件

import {memo} form react
const MemoChild = memo(Child)

 const Aaa = () => {
  const [count, setCount] = useState(1);
  return (
      <div>
          <h2>{count}</h2>
          <button onClick={() => setCount(count + 1)}>+1</button>
          <MemoChild />
      </div>
  )
}

useMemo

Qs:一个组件内 某个返回值因state变化组件更新 导致多次触发

Rs:使用useMemo包裹函数

不想谁渲染就包裹谁

const Aaa = () => {
   console.log('组件更新了')
  const [count, setCount] = useState(1);
const [val, setValue] = useState('');
const expensive = useMemo(() => {
    console.log('compute');
    let sum = 0;
    for (let i = 0; i < count * 100; i++) {
        sum += i;
    }
    return sum;
},[count])

return <div>
    <h4>{count}-{expensive}</h4>
    {val}
    <div>
        <button onClick={() => setCount(count + 1)}>+c1</button>
        <input value={val} onChange={event => setValue(event.target.value)}/>
    </div>
</div>;
}
// 这里的expensive函数依赖count
// 但是input输入时 改变Setval 导致组件更新 

useCallback

Qs: 在向子组件传递函数props时,每次 render 都会创建新函数,导致子组件不必要的渲染

使用useCallback无论 render 多少次,我们的函数都是同一个函数,减小不断创建的开销。

const MemoTestChild = memo(TestChild);
function Test() {
    const [count, setCount] = useState(100);
    const [name, setName] = useState('TestChild组件');
    return (
        <>
            <h2>{count}</h2>
            <button onClick={() => setCount(count + 1)}>++</button>
            {/* <MemoTestChild name={name} setName={(newName) => setName(newName)} /> */}
        // 重要的这一行代码, 确保只有当子组件调用setName时才更新子组件
            <MemoTestChild name={name} setName={useCallback((newName) => setName(newName),[])} />
        </>
    )
}

//子组件部分
 
function TestChild ( {name, setName} ) {
  
  console.log('子组件更新');
  return (
      <>
          <h3>子组件:{name}</h3>
          <button onClick={() => setName(name + '改变后的name')}>改变name</button>
      </>
  );
}

路由

/home 下建立新的子路由 '/home/app' ,使用跳转history.push( pathname: "/home/app")有时会出现问题, 根据路由模糊匹配, /home也会被匹配到 , 如果只想显示/home/app组件需要使用精准匹配, 这里使用一种不用精准匹配的办法, 将'/home/app'组件注册在'/home'上面, 这样路由匹配时会先匹配到'/home/app' 注意这里不是父子级, 是平级关系

页面跳转

方法一: 引入LINK组件, 用 <Link to={’/log’}> 来跳转, 参数可以拼接在地址栏后面, 拿参数可以使用动态路由匹配后的地址栏中取参。

//入参
<Link to={`collectorDetail/${record.Id}`} className="margin-l2">设备管理</Link>
//取参
 const { configId, roomId } = props.match.params;

方法二: 使用props.history.push 传参,和方法一不同的是,参数不会拼接在地址栏后, 需要注意的是,组件的props.hitstory是不是undefined, 如果是,引入withRouter组件包裹当前组件即可。
import { withRouter } from 'react-router-dom'
export default withRouter(XXX);

// 入参
props.history.push({
      pathname: `/log`,
      query: 33333,
    }// 取参
const params = props.location.query

样式

在修改了antd组件样式后, 使用侧边栏切换到其他页面, 因为已经载入了上一个样式文件,导致该组件所有样式都变化, 刷新之后正常, 因为载入的文件被刷新掉了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值