(property) React.MutableRefObject<null>.current: null这种报错怎么处理?

理解React中的useRefHook与类型注解
本文介绍了在React中使用TypeScript时,如何对`useRef`hook的返回值进行类型注解,特别是`React.MutableRefObject<null>`的用法。文章强调了在访问`.current`属性前进行空值检查的重要性,以防止空引用异常,并提出了几种处理方法,包括设置非空初始值、使用空值检查、可选链式操作符以及默认值策略。

这段代码是 TypeScript 的类型注解,表示一个名为 `current` 的可变引用类型变量,它的初始值为 `null`。

在 React 中,`useRef` hook 返回的是一个可变引用类型变量,因此需要使用 `React.MutableRefObject` 类型来进行类型注解。该类型定义在 React 的声明文件中,并且具有一个泛型参数,用于指定引用变量的类型。

在这个例子中,`React.MutableRefObject<null>` 表示一个可变引用类型变量,其初始值为 `null`。使用 `.current` 属性可以访问该引用变量当前所指向的值。由于初始值为 `null`,因此在访问 `.current` 属性之前,需要先进行空值检查,以避免出现空引用异常。

这种问题可能会出现在使用 React 的 `useRef` hook 或类组件中,当使用引用类型变量时,需要确保其初始值不为 `null`,以避免出现空引用异常。

有一些处理方法可以避免这种问题的出现:

1. 在创建引用类型变量时,指定一个非空的初始值,例如:

```

const myRef = useRef<any>(0);

```

2. 在访问引用类型变量时,使用空值检查避免出现空引用异常,例如:

```

if (myRef.current) {

  // do something with myRef.current

}

```

3. 使用可选链式操作符(Optional Chaining Operator)可以在遇到空引用时,自动返回 `undefined`,避免出现异常,例如:

```

const myValue = myRef?.current?.prop1?.prop2;

```

在这个例子中,如果 `myRef` 或 `myRef.current` 为空,则返回 `undefined`,而不会抛出异常。

4. 使用默认值或条件语句,为可能为空的引用类型变量提供一个默认值或异常处理逻辑,例如:

```

const myValue = myRef.current || defaultValue;

```

在这个例子中,如果 `myRef.current` 为空,则使用 `defaultValue` 来代替。

import React, { useState } from 'react'; import { Select, DatePicker, TimePicker, Button, Table, Modal, Form, Input, ConfigProvider, Space, message, Spin, } from 'antd'; import { FormOutlined, PlusCircleOutlined, MinusCircleOutlined } from '@ant-design/icons'; import locale from 'antd/locale/zh_CN'; import dayjs, { Dayjs } from 'dayjs'; import 'dayjs/locale/zh-cn'; import { useSetPublish, useGetPublishHistory } from './request'; import type { PublishParams, PublishFormValues } from '@/types/autoPublish'; import {PROJECT_NAMES, statusMap, envMap, typeMap} from './config'; import { useQueryClient } from '@tanstack/react-query'; dayjs.locale('zh-cn'); const AutoPublish: React.FC = () => { const queryClient = useQueryClient(); const { data, isLoading } = useGetPublishHistory({ pageSize: '10', page: '1', }); const publishHistory = data?.data || []; const [messageApi, contextHolder] = message.useMessage(); const [chooseDate, setChooseDate] = useState<Dayjs | null>(dayjs()); const [chooseTime, setChooseTime] = useState<Dayjs | null>(dayjs()); const [isModalVisible, setIsModalVisible] = useState(false); const [form] = Form.useForm(); /** * 整合时间值 */ const handlePublishDate = (publishDate: Dayjs, publishTime: Dayjs) => { if (publishDate && publishTime) { const fullTime = publishDate .hour(publishTime.hour()) .minute(publishTime.minute()) .second(0); const formattedTime = fullTime.format('YYYY-MM-DD HH:mm:ss'); return formattedTime } } const { mutate } = useSetPublish(); /** * 新建发布 */ const newPublish = () => { setIsModalVisible(true); }; /** * 修改发布 */ const revisePublish = (record: PublishParams) => { const projects = record.product.map((item: any) => ({ productName: item.product_name, productTag: item.product_tag, })); // 解析发布时间 const publishTime = dayjs(record.publish_time); // 设置表单值 form.setFieldsValue({ projects: projects, branchVersion: record.branch_version, productEnv: record.publish_env.toString(), publishTime: record.publish_time, }); // 设置日期和时间选择器的值 setChooseDate(publishTime); setChooseTime(publishTime); setIsModalVisible(true); }; /** * 发布 */ const handleSubmit = (type: string) => { form.validateFields().then((values: PublishFormValues) => { if (!chooseDate || !chooseTime) { alert('请选择完整的日期和时间'); return; } const publishTime = handlePublishDate(chooseDate, chooseTime); const { projects, branchVersion, productEnv } = values; const publishType = type === 'schedule' ? 1 : 2; const publishStatus = 2; const params = { product: projects.map((p) => ({ product_name: p.productName, product_tag: p.productTag, })), branch_version: branchVersion, publish_time: publishTime!, publish_user: 'admin', publish_status: publishStatus, publish_type: publishType, publish_env: Number(productEnv), }; console.log(`【${type}】提交数据:`, params); mutate(params, { onSuccess: () => { messageApi.open({ type: 'success', content: '发布成功!', }); // 刷新列表 queryClient.invalidateQueries({ queryKey: ['publishHistory'], }); }, onError: (data) => { messageApi.open({ type: 'error', content: data.message || '发布失败~请重试!', }); } }); setIsModalVisible(false); }); }; /** * 禁用今天之前的日期 */ const disabledDate = (current: Dayjs) => { return current < dayjs().startOf('day'); }; /** * 禁用此刻之前时间点 */ const disabledDateTime = (current: Dayjs | null) => { const now = dayjs(); if(!current) return; if (current.isSame(now, 'day')) { return { disabledHours: () => range(0, now.hour()), disabledMinutes: (selectedHour: number) => selectedHour === now.hour() ? range(0, now.minute()) : [], }; } return {}; }; const range = (start: number, end: number) => { const result = []; for (let i = start; i < end; i++) { result.push(i); } return result; }; /** * 定义表格 */ const columns1 = [ { title: '发布时间', dataIndex: 'publish_time', key: 'publish_time', }, { title: '发布版本', dataIndex: 'branch_version', key: 'branch_version', }, { title: '发布状态', dataIndex: 'publish_status', key: 'publish_status', render: (status: number) => statusMap[status] || '未知类型', }, { title: '发布类型', dataIndex: 'publish_type', key: 'publish_type', render: (type: number) => typeMap[type] || '未知类型', }, { title: '发布环境', dataIndex: 'publish_env', key: 'publish_env', render: (text: number) => envMap[text] || '未知环境', }, { title: '编辑', key: 'edit', render: (_: any, record: PublishParams) => ( <FormOutlined onClick={() => revisePublish(record)} style={{ cursor: 'pointer' }} /> ), }, ]; const columns2 = [ { title: '项目名称', dataIndex: 'product_name', key: 'product_name', }, { title: 'Tag号', dataIndex: 'product_tag', key: 'product_tag', }, ]; if (isLoading) { return <Spin tip="Loading">{<a>加载中...</a>}</Spin>; } return ( <div style={{ padding: '20px' }}> {contextHolder} {/* 控件 */} <div style={{ display: 'flex', justifyContent: 'flex-end', gap: '16px', marginBottom: '20px' }}> <Button type="primary" onClick={newPublish} style={{ backgroundColor: '#3662ec' }}> 新建发布 </Button> </div> {/* 表格 */} <Table columns={columns1} dataSource={publishHistory} rowKey={(record) => record.branch_version} expandable={{ expandedRowRender: (record) => ( <Table columns={columns2} dataSource={record.product || []} pagination={false} style={{ width: 500 }} rowKey={(item, index) => index!} /> ), }} /> {/* 编辑模态框 */} <Modal title="项目信息" open={isModalVisible} width={700} onCancel={() => setIsModalVisible(false)} footer={[ <Button key="back" onClick={() => setIsModalVisible(false)}> 取消 </Button>, <Button key="schedule" type="primary" onClick={() => handleSubmit('schedule')}> 预约发布 </Button>, <Button key="publish" type="primary" onClick={() => handleSubmit('rightNow')}> 立即发布 </Button>, ]} > <Form form={form} layout="horizontal" labelCol={{ span: 5 }} wrapperCol={{ span: 10 }} initialValues={{ projects: [ { productName: undefined, productTag: '', }, ], }} > <Form.List name="projects"> {(fields, { add, remove }) => ( <> {fields.map(({ key, name, ...restField }) => ( <Space key={key} style={{ display: 'flex', marginBottom: 0 }} align="baseline"> <Form.Item {...restField} name={[name, 'productName']} label="项目名称" labelCol={{ span: 9 }} wrapperCol={{ span: 16 }} rules={[{ required: true, message: '请选择项目名称' }]} > <Select showSearch optionFilterProp="children" options={PROJECT_NAMES.map((name) => ({ label: name, value: name, }))} style={{ width: 250 }} /> </Form.Item> <Form.Item {...restField} name={[name, 'productTag']} label="Tag" labelCol={{ span: 10 }} wrapperCol={{ span: 50 }} rules={[{ required: true, message: '请输入 Tag' }]} > <Input /> </Form.Item> <MinusCircleOutlined onClick={() => remove(name)} /> </Space> ))} <div style={{ display: 'flex', justifyContent: 'center', marginBottom: 16 }}> <Button type="dashed" onClick={() => add()} icon={<PlusCircleOutlined />} style={{ width: 200, height: 35, borderRadius: 6, display: 'flex', alignItems: 'center', justifyContent: 'center' }} > 添加项目 </Button> </div> </> )} </Form.List> <Form.Item label="选择版本" name="branchVersion" rules={[{ required: true }]}> <Input /> </Form.Item> <Form.Item label="选择环境" name="productEnv" rules={[{ required: true }]}> <Select style={{ width: 150 }}> <Select.Option value="1">测试环境</Select.Option> <Select.Option value="2">生产环境</Select.Option> </Select> </Form.Item> <Form.Item label={ <span> <span style={{ fontSize: '18px', color: 'rgb(255, 141, 142)'}}>*</span> 选择时间 </span> } > <Space> <ConfigProvider locale={locale}> <DatePicker format="YYYY-MM-DD" value={chooseDate} disabledDate={disabledDate} onChange={(date) => setChooseDate(date ? dayjs(date) : null)} /> </ConfigProvider> <TimePicker format="HH:mm" value={chooseTime} disabledTime={() => disabledDateTime(chooseDate)} onChange={(time) => setChooseTime(time ? dayjs(time) : null)} /> </Space> </Form.Item> </Form> </Modal> </div> ); }; export default AutoPublish; 其中,TimePicker的disabledTime ts类型报错如下: Type '() => { disabledHours: () => number[]; disabledMinutes: (selectedHour: number) => number[]; } | { disabledHours?: undefined; disabledMinutes?: undefined; } | undefined' is not assignable to type '(date: Dayjs) => DisabledTimes'. Type '{ disabledHours: () => number[]; disabledMinutes: (selectedHour: number) => number[]; } | { disabledHours?: undefined; disabledMinutes?: undefined; } | undefined' is not assignable to type 'DisabledTimes'. Type 'undefined' is not assignable to type 'DisabledTimes'.ts(2322) interface.d.ts(139, 5): The expected type comes from property 'disabledTime' which is declared here on type 'IntrinsicAttributes & Omit<TimePickerProps, "ref"> & RefAttributes<PickerRef>' (property) disabledTime?: ((date: dayjs.Dayjs) => DisabledTimes) | undefined
最新发布
09-03
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值