如果我真的必须(比如通过 WebAPI 保存设备连接),我如何在不破坏它的情况下将不可序列化的变量存储在 react - redux 状态中
How do I store a non serialisable variable in a react - redux state without breaking it if I really must (like saving a device connection via WebAPIs)
我几乎在每个 google 搜索结果中都收到声明“不要在您的状态下保存不可序列化的变量” - 但是当我真的应该时会发生什么?
Progect: 我正在为通过串行端口连接的设备S 构建一个应用程序(使用SerialPort WebAPI)。
我希望保存连接实例,因为我在我的所有应用程序中都使用它,老实说,我厌倦了在需要时上下传递实例,而不知道重新呈现数据和显示新数据的反应——这对我来说也很重要.
我完成的步骤:
使用serializableCheck: false
:
很容易忽略不可序列化的错误
导出默认配置存储({
reducer: {
serialport: SerialPortDevicesReducer,
bluetooth: BluetoothDevicesReducer,
},
middleware: getDefaultMiddleware =>
getDefaultMiddleware({
thunk,
serializableCheck: false
}).concat(logger),})
但是现在我面临着一个大问题:
每当我创建连接时,我都会得到处理连接的特定 SerialPort 设备对象的对象。
deviceReducer:{
编号:1,
实例:SerialPort{[此处的属性和方法]},
...
}
每当我使用 open()
、write()
或 read()
之类的方法时,它都会更改主连接实例对象并因已知错误而中断:
Error: Invariant failed: A state mutation was detected between
dispatches, in the path 'serialport.0.instance.readable'. This may
cause incorrect behavior
- 因为它不可序列化,所以我不能克隆它(我认为这是原因?)然后重新分配它 + 我认为克隆连接实例会导致其他设备连接问题。
我最终直接在 state 中编写了连接方法案例,并使用“promise”新变量来处理结果。
// click in a react component
const handleConnect = () => {
try {
if ( dispatch(connect(device)) ) {
setActiveStep((prevActiveStep) => prevActiveStep + 1)
return true
}
}
catch (e) {
console.error("Device cannot connect: ", e)
}
}
// In a file that trigges dispatch() to the reduces
const connect = (deviceId) => async (dispatch, getState) => {
try {
dispatch({
type: "serialport/connect",
payload: deviceId
})
} catch(e) {
console.log(e)
}
}
// in reducer
const SerialPortDevicesReducer = (state = initialState, action) => {
switch (action.type) {
case 'serialport/connect':
try {
return {
...state,
[action.payload]: {
...state[action.payload],
promise: state[action.payload].instance.open({baudRate: 115200})
}
}
} catch (e) {
console.error("Cannot run promise inside reducer: ", e)
}
这是我目前找到的唯一解决方法。这基本上迫使我在 reducer 中处理(可能是一些复杂的)事情,而不仅仅是将数据传递给它。我尝试对写入方法应用相同的方法:
// click in component
const handleExecute = (command) => {
try {
dispatch(writeToSP(device1.device, command))
} catch (e) {
console.log(e)
}
}
// In file which trigges the dispatch()
const writeToSP = (deviceId, command = "Z !\n") => async (dispatch) => {
let startTime = new Date().getTime()
let encoder = new TextEncoder()
try {
dispatch({
type: "serialport/write",
payload: {
id: deviceId,
// cmd: encoder.encode(command),
// startTime
}
})
} catch (e) {
console.error("error writing: ", e)
}
}
// in reducer
...
case 'serialport/write':
try {
const writer = state[action.payload.id].instance.writable.getWriter()
} catch (e) {
console.error("Cannot run promise inside reducer: ", e)
}
再次出现“错误:不变失败:检测到状态突变...”的错误,我猜测这是它更改了 SerialPort 实例中的其他属性的结果。
拥有像 redux-promise-middleware
这样的包很棒,但在我的状态下似乎是一个对象负责自己的承诺和更改。
我该如何处理这种特殊情况?
很简单:不要把它放到 Redux 中。 Redux 是为数据而生,而不是为任意外部 libraries/dependency 注入而生。
如果该值在初始化后永远不会改变,并且您在 React 之外进行初始化,只需将其放入导出的全局变量中即可。
如果这个值会随着时间变化,你应该使用React的依赖注入机制:Context。这确实是上下文的用途 - 不是共享状态值,而是全局依赖性。
我几乎在每个 google 搜索结果中都收到声明“不要在您的状态下保存不可序列化的变量” - 但是当我真的应该时会发生什么?
Progect: 我正在为通过串行端口连接的设备S 构建一个应用程序(使用SerialPort WebAPI)。 我希望保存连接实例,因为我在我的所有应用程序中都使用它,老实说,我厌倦了在需要时上下传递实例,而不知道重新呈现数据和显示新数据的反应——这对我来说也很重要.
我完成的步骤:
使用
很容易忽略不可序列化的错误serializableCheck: false
:导出默认配置存储({
reducer: { serialport: SerialPortDevicesReducer, bluetooth: BluetoothDevicesReducer, }, middleware: getDefaultMiddleware => getDefaultMiddleware({ thunk, serializableCheck: false }).concat(logger),})
但是现在我面临着一个大问题:
每当我创建连接时,我都会得到处理连接的特定 SerialPort 设备对象的对象。
deviceReducer:{ 编号:1, 实例:SerialPort{[此处的属性和方法]}, ... }
每当我使用
open()
、write()
或read()
之类的方法时,它都会更改主连接实例对象并因已知错误而中断:
Error: Invariant failed: A state mutation was detected between dispatches, in the path 'serialport.0.instance.readable'. This may cause incorrect behavior
- 因为它不可序列化,所以我不能克隆它(我认为这是原因?)然后重新分配它 + 我认为克隆连接实例会导致其他设备连接问题。
我最终直接在 state 中编写了连接方法案例,并使用“promise”新变量来处理结果。
// click in a react component
const handleConnect = () => {
try {
if ( dispatch(connect(device)) ) {
setActiveStep((prevActiveStep) => prevActiveStep + 1)
return true
}
}
catch (e) {
console.error("Device cannot connect: ", e)
}
}
// In a file that trigges dispatch() to the reduces
const connect = (deviceId) => async (dispatch, getState) => {
try {
dispatch({
type: "serialport/connect",
payload: deviceId
})
} catch(e) {
console.log(e)
}
}
// in reducer
const SerialPortDevicesReducer = (state = initialState, action) => {
switch (action.type) {
case 'serialport/connect':
try {
return {
...state,
[action.payload]: {
...state[action.payload],
promise: state[action.payload].instance.open({baudRate: 115200})
}
}
} catch (e) {
console.error("Cannot run promise inside reducer: ", e)
}
这是我目前找到的唯一解决方法。这基本上迫使我在 reducer 中处理(可能是一些复杂的)事情,而不仅仅是将数据传递给它。我尝试对写入方法应用相同的方法:
// click in component
const handleExecute = (command) => {
try {
dispatch(writeToSP(device1.device, command))
} catch (e) {
console.log(e)
}
}
// In file which trigges the dispatch()
const writeToSP = (deviceId, command = "Z !\n") => async (dispatch) => {
let startTime = new Date().getTime()
let encoder = new TextEncoder()
try {
dispatch({
type: "serialport/write",
payload: {
id: deviceId,
// cmd: encoder.encode(command),
// startTime
}
})
} catch (e) {
console.error("error writing: ", e)
}
}
// in reducer
...
case 'serialport/write':
try {
const writer = state[action.payload.id].instance.writable.getWriter()
} catch (e) {
console.error("Cannot run promise inside reducer: ", e)
}
再次出现“错误:不变失败:检测到状态突变...”的错误,我猜测这是它更改了 SerialPort 实例中的其他属性的结果。
拥有像 redux-promise-middleware
这样的包很棒,但在我的状态下似乎是一个对象负责自己的承诺和更改。
我该如何处理这种特殊情况?
很简单:不要把它放到 Redux 中。 Redux 是为数据而生,而不是为任意外部 libraries/dependency 注入而生。
如果该值在初始化后永远不会改变,并且您在 React 之外进行初始化,只需将其放入导出的全局变量中即可。
如果这个值会随着时间变化,你应该使用React的依赖注入机制:Context。这确实是上下文的用途 - 不是共享状态值,而是全局依赖性。