reducer 中的类型问题,使用 useReducer 的 typescript
Posted
技术标签:
【中文标题】reducer 中的类型问题,使用 useReducer 的 typescript【英文标题】:Type problems in reducer , typescript with useReducer 【发布时间】:2021-11-13 19:23:26 【问题描述】:我在使用 TypeScript 时遇到类型问题,在使用 React 钩子 useReducer
时,控制台会引发以下错误:
函数缺少结束返回语句并且返回类型不包括“未定义”。 TS2366
5 | const Reducer = (state: InitialStateType, action: Action): InitialStateType =>
在执行操作之前,我尝试将类型分配给提交句柄返回的值。问题似乎与我的服务或减速器无关。我真的希望你能指导我......
reducerSensor.ts
import InitialStateType from "../interfaces/interfaces"
import Action from "../types/types";
const Reducer = (state: InitialStateType, action: Action) : InitialStateType =>
switch (action.type)
case 'REQUEST_GET':
return
...state,
loading: true,
;
case 'SUCCESS_GET':
return
...state,
loading: false,
sensors: state.sensors.concat(action.payload)
;
case 'ERROR_GET':
return
...state,
loading: false,
error: action.error,
;
case 'REQUEST_CREATE' :
return
...state,
loading: true,
;
case 'SUCCESS_CREATE' :
return
...state,
loading: false,
sensors: [...state.sensors, action.payload]
;
case 'ERROR_CREATE':
return
...state,
loading: false,
error: action.error,
;
default:
throw new Error('Error in the change of state');
;
export default Reducer;
ListSensor.ts
import FC ,useEffect , useReducer from 'react'
import '../../styles/ListSensors.css'
import FormSensors from '../FormSensors/FormSensors'
import * as sensorServices from '../../services/sensorServices'
import Reducer from '../../Reducers/reducerSensor'
import InitialStateType, Sensor from '../../interfaces/interfaces'
const ListSensors: FC<htmlDataListElement>= () =>
const initState: InitialStateType =
sensors: [],
loading: true,
error: false
const [state, dispatch] = useReducer(Reducer, initState )
const sensors, loading, error = state;
useEffect(() =>
dispatch( type: 'REQUEST_GET' );
const loadSensors = async () =>
const res = await sensorServices.getSensors();
if (res.status === 200)
dispatch( type: 'SUCCESS_GET', payload: res.data );
return;
dispatch( type: 'ERROR_GET', error: res.data);
;
loadSensors();
, []);
const handleCreate = (objectSensor : Sensor) =>
dispatch( type: 'REQUEST_CREATE' );
const createSensor = async () =>
const res = await sensorServices.createNewSensor(objectSensor);
if (res.status === 200)
dispatch( type: 'SUCCESS_CREATE', payload: res.data );
return;
dispatch( type: 'ERROR_CREATE', error: res.data);
;
createSensor();
return (
<div>
loading ? (
<p>loading...</p>
) : error ? (
<p>error</p>
) : (
<div className='datatable-container'>
<table className="datatable">
<thead>
<tr>
<th>Status</th>
<th>Name</th>
<th>Ubication (lat / lng)</th>
<th>MinVal</th>
<th>MaxVal</th>
<th>Events</th>
<th>Options</th>
</tr>
</thead>
<tbody>
<FormSensors event=handleCreate/>
sensors && sensors.length > 0 ? sensors.map((sensor: Sensor) => (
<tr key=sensor._id>
<td><span className=sensor.active ? "active" : "inactive"></span></td>
<td>sensor.name</td>
<td>sensor.ubication[0]° lat , sensor.ubication[1]° lng</td>
<td>sensor.minVal°</td>
<td>sensor.maxVal°</td>
<td><button className="button button-event"><i className="material-icons">sensors</i></button></td>
<td> <button className="button button-delete"><i className="material-icons">delete</i></button> <button className="button button-update"><i className="material-icons">edit</i></button></td>
</tr>
)
) : null
</tbody>
</table>
</div>
)
</div>
)
export default ListSensors
SensorServices.ts
import axios from "axios";
import Sensor from "../interfaces/interfaces";
const API = "http://localhost:3001";
export const getSensors = async () =>
return await axios.get(`$API/sensors`);
;
export const createNewSensor = async (sensor: Sensor) =>
return await axios.post(`$API/sensor`, sensor);
;
export const deleteSensorById = async (id: string) =>
return await axios.delete(`$API/sensors/$id`);
;
export const updateSensor = async (id: string, sensor: Sensor) =>
return await axios.put(`$API/sensors/$id`, sensor);
;
types.ts
import ChangeEvent from 'react';
import Sensor from '../interfaces/interfaces';
export type Action =
type: 'REQUEST_GET'
| type: 'SUCCESS_GET', payload: Sensor
| type: 'ERROR_GET' , error: string
| type: 'ERROR_CREATE' , error: string
| type: 'REQUEST_CREATE'
| type: 'SUCCESS_CREATE', payload: Sensor
export type InputChange = ChangeEvent<HTMLInputElement | HTMLTextAreaElement>;
export type SelectChange = ChangeEvent<HTMLSelectElement >
interfaces.ts
export interface Sensor
_id?: string;
name: string;
ubication: number[]
maxVal: number;
minVal: number;
active: boolean;
createdAt?: string | Date;
updatedAt?: string | Date;
export interface SensorForm
name: string;
lat: string;
lng: string;
active: boolean;
export interface InitialStateType
sensors: Sensor[],
loading: Boolean,
error: Boolean | String
【问题讨论】:
尝试在 ts 操场上分享最小可重现示例。会更容易回答 【参考方案1】:作为最佳实践,尝试添加default
case,它返回未触及状态(不一定需要抛出错误)。
import InitialStateType from "../interfaces/interfaces"
import Action from "../types/types";
const Reducer = (state: InitialStateType, action: Action) : InitialStateType =>
switch (action.type)
case 'REQUEST_GET':
return
...state,
loading: true,
;
case 'SUCCESS_GET':
return
...state,
loading: false,
sensors: state.sensors.concat(action.payload)
;
case 'ERROR_GET':
return
...state,
loading: false,
error: action.error,
;
case 'REQUEST_CREATE' :
return
...state,
loading: true,
;
case 'SUCCESS_CREATE' :
return
...state,
loading: false,
sensors: [...state.sensors, action.payload]
;
case 'ERROR_CREATE':
return
...state,
loading: false,
error: action.error,
;
default:
return state;
;
export default Reducer;
【讨论】:
【参考方案2】:在:
const Reducer = (state: InitialStateType, action: Action) : InitialStateType =>
switch (action.type)
case 'REQUEST_GET':
return ...state, loading: true, ;
case 'SUCCESS_GET':
return
...state,
loading: false,
sensors: state.sensors.concat(action.payload)
;
case 'ERROR_GET':
return ...state, loading: false, error: action.error, ;
case 'REQUEST_CREATE' :
return ...state, loading: true, ;
case 'SUCCESS_CREATE' :
return ...state, loading: false, sensors: [...state.sensors, action.payload] ;
case 'ERROR_CREATE':
return
...state,
loading: false,
error: action.error,
;
// ADD A DEFAULT CLAUSE HERE <---------------
;
您的switch
应该有一个default
子句。否则 TypeScript 推断它可能会返回 undefined
,因为如果 action.type
与任何 case
都不匹配,就会发生这种情况。
如果您没有该场景的default
值,您可以简单地使用throw new Error()
:
case 'ERROR_CREATE':
return
...state,
loading: false,
error: action.error,
;
default:
throw new Error('some meaningful message (optional)');
;
编辑:
类型以这种方式工作得很好。见typescript playground demo。
【讨论】:
不工作 :( ,同样的错误 你到底做了什么?您可以发布Reducer
的更新代码吗?
是的!外观已更新
尝试重新启动您的 tsc 或 IDE。有些东西必须被缓存/不编译,因为打字工作,检查我刚刚编辑的答案底部的链接以上是关于reducer 中的类型问题,使用 useReducer 的 typescript的主要内容,如果未能解决你的问题,请参考以下文章
使用 Redux 如何阻止 reducer 中的 switch 增长?