[英]Converting from class to functional component with async state setting
I have a simple class-based component that I'm trying to convert to a function-based component, but am running into all kinds of dead ends. 我有一个简单的基于类的组件,我试图将其转换为基于函数的组件,但遇到了各种各样的死胡同。
My component is a straightforward adaptation of the boilerplate gifted-chat
package, and uses Watson Assistant as a backend to provide responses. 我的组件是对样板
gifted-chat
程序包的直接改编,并使用Watson Assistant作为后端来提供响应。 There's nothing complex about the backend part, these are just thin wrappers on Watson Assistants's API: 后端部分没有什么复杂的,它们只是Watson Assistants API上的薄包装:
getSessionID = async (): Promise<string>
gets a session ID for use in communicating with the backend, and 获取用于与后端通信的会话ID,以及
sendReply = async (reply: string, sessionID: string): Promise<string>
returns Assistant's response to the string provided as a reply
. 将Assistant的响应返回给作为
reply
的字符串。 These are not the source of the trouble I'm having (the bodies of both could be replaced with return await "some string"
and I'd have the same issues): the class-based version (below) works perfectly. 这些不是我遇到的麻烦的根源(可以将它们的主体替换为
return await "some string"
而我会遇到相同的问题):基于类的版本(如下)可以正常工作。
But I'm at a loss to figure out how to convert this to a functional form, in particular: 但是我不知该如何将其转换为功能形式,特别是:
componentWillMount
. componentWillMount
替代品。 Using useEffect
with sessionID
as state results in errors: getMessage
gets called (even if I await
) before the required sessionID is set. useEffect
和sessionID
作为状态会导致错误:在设置所需的sessionID之前,将调用getMessage
(即使我正在await
)。 I can avoid this by not making sessionID state (which it arguably shouldn't be) and just making it a global (as in the functional attempt below). 我可以通过不设置sessionID状态(可以说不应设置),而只是将其设置为全局状态(如下面的功能尝试)来避免这种情况。 But even if I do this:
但是即使我这样做:
Both of these problems are, I think, linked to the lack of callbacks in the hook-based state setting idiom, but the issue could also lie elsewhere. 我认为,这两个问题都与基于钩子的状态设置惯用法中缺少回调函数有关,但问题还可能出在其他地方。 In any case, I'm at a loss to know what to do.
无论如何,我不知所措。
Chatter.tsx (working class based version) Chatter.tsx (基于工作类的版本)
import React from 'react'
import { GiftedChat } from 'react-native-gifted-chat'
import WatsonAssistant from "../services/WatsonAssistant"
class Chatter extends React.Component {
state = {
messages: [],
sessionID: null,
}
componentWillMount() {
WatsonAssistant.getSessionID()
.then((sID) => {
this.setState( {
sessionID: sID,
} )
} )
.then(() => this.getMessage(''))
.catch((error) => {
console.error(error)
} )
}
onSend = (message = []): void => {
this.setState((previousState) => ( {
messages: GiftedChat.append(previousState.messages, message),
} ), () => {
this.getMessage(message[0].text.replace(/[\n\r]+/g, ' '))
} )
}
getMessage = async (text: string): Promise<void> => {
let response = await WatsonAssistant.sendReply(text, this.state.sessionID)
let message = {
_id: Math.round(Math.random() * 1000000).toString(),
text: response,
createdAt: new Date(),
user: {
_id: '2',
name: 'Watson Assistant',
},
}
this.setState((previousState) => ( {
messages: GiftedChat.append(previousState.messages, message),
} ))
}
render() {
return (
<GiftedChat
messages={ this.state.messages }
onSend={ messages => this.onSend(messages) }
user={ {
_id: 1,
} }
/>
)
}
}
export default Chatter
Chatter.tsx (failed function based attempt) Chatter.tsx (基于函数的失败尝试)
import React, {FC, ReactElement, useEffect, useState } from 'react'
import { GiftedChat } from 'react-native-gifted-chat'
import WatsonAssistant from "../services/WatsonAssistant"
let sessionID: string
const Chatter: FC = (): ReactElement => {
const [ messages, setMessages ] = useState([])
useEffect(() => {
const fetchData = async () => {
WatsonAssistant.getSessionID()
.then(sID => sessionID = sID )
.then(() => getMessage(''))
.catch((error) => {
console.error(error)
} )
}
fetchData()
}, [ ])
const onSend = async (message = []) => {
const newMessages = await GiftedChat.append(messages, message)
await setMessages(newMessages)
await getMessage(message[0].text.replace(/[\n\r]+/g, ' '))
}
const getMessage = async (text: string): Promise<void> => {
let response = await WatsonAssistant.sendReply(text, sessionID)
let message = {
_id: Math.round(Math.random() * 1000000).toString(),
text: response,
createdAt: new Date(),
user: {
_id: '2',
name: 'Watson Assistant',
},
}
await setMessages(await GiftedChat.append(messages, message))
}
return (
<GiftedChat
messages={ messages }
onSend={ messages => onSend(messages) }
user={ {
_id: 1,
} }
/>
)
}
export default Chatter
Chatter.tsx (working function based version) Chatter.tsx (基于工作功能的版本)
import React, {FC, ReactElement, useEffect, useState } from 'react'
import { GiftedChat } from 'react-native-gifted-chat'
import WatsonAssistant from "../services/WatsonAssistant"
let sessionID: string
const Chatter: FC = (): ReactElement => {
const [ messages, setMessages ] = useState([])
useEffect(() => {
const fetchData = async () => {
WatsonAssistant.getSessionID()
.then(sID => sessionID = sID )
.then(() => getMessage('', []))
.catch((error) => {
console.error(error)
} )
}
fetchData()
}, [ ])
const onSend = async (message = []) => {
const newMessages = await GiftedChat.append(messages, message)
await setMessages(newMessages) // Apparently, no waiting goes on here
await getMessage(message[0].text.replace(/[\n\r]+/g, ' '), newMessages)
}
const getMessage = async (text: string, currentMessages): Promise<void> => {
let response = await WatsonAssistant.sendReply(text, sessionID)
let message = {
_id: Math.round(Math.random() * 1000000).toString(),
text: response,
createdAt: new Date(),
user: {
_id: '2',
name: 'Watson Assistant',
},
}
await setMessages(await GiftedChat.append(currentMessages, message))
}
return (
<GiftedChat
messages={ messages }
onSend={ messages => onSend(messages) }
user={ {
_id: 1,
} }
/>
)
}
export default Chatter
Ok, since I don't have your full code I'm not sure this will just work as-is (in particular without the types from your dependencies I'm not sure if/how much the compiler will complain), but should give you something you can adapt easily enough. 好的,因为我没有完整的代码,所以我不确定这是否可以按原样工作(特别是如果没有依赖项中的类型,我不确定编译器是否/会抱怨多少),但是应该给出您可以轻松地进行调整。
const reducer = ({ messages }, action) => {
switch (action.type) {
case 'add message':
return {
messages: GiftedChat.append(messages, action.message),
};
case 'add sent message':
return {
// Not sure if .append is variadic, may need to adapt
messages: GiftedChat.append(messages, action.message, action.message[0].text.replace(/[\n\r]+/g, ' ')),
}
}
};
const Chatter = () => {
const [sessionID, setSessionID] = useState(null);
const [messages, dispatch] = useReducer(reducer, []);
const getMessage = async (text: string, sessionID: number, type: string = 'add message'): Promise<void> => {
const response = await WatsonAssistant.sendReply(text, sessionID);
const message = {
_id: Math.round(Math.random() * 1000000).toString(),
text: response,
createdAt: new Date(),
user: {
_id: '2',
name: 'Watson Assistant',
},
};
dispatch({
type,
message,
});
};
useEffect(() => {
const fetchData = async () => {
WatsonAssistant.getSessionID()
.then(sID => (setSessionID(sID), sID))
.then(sID => getMessage('', sID))
.catch((error) => {
console.error(error)
});
}
fetchData();
}, []);
return (
<GiftedChat
messages={messages}
onSend={messages => getMessage(messages, sessionID, 'add sent message')}
user={{
_id: 1,
}}
/>
);
};
Main difference is useReducer
. 主要区别是
useReducer
。 As far as I can tell in the original code you had two actions: append this message or append this message and then a copy of it with the text regex replaced. 据我在原始代码中所知道的,您有两个操作:追加此消息或追加此消息,然后用文本正则表达式替换它的副本。 I've used different dispatches to the reducer to handle the cases rather than the callback to
setState
. 我使用了不同的调度程序来处理化例,而不是使用
setState
的回调。 I've modified your attempt at useEffect
, here I'm (ab)using the comma operator to return the ID returned from the service so that it can be fed directly to getMessage
as a parameter rather than relying on state that hasn't been updated yet. 我已经修改了您对
useEffect
的尝试,在这里,我正在(ab)使用逗号运算符返回从服务返回的ID,以便可以将其直接作为参数传递给getMessage
,而不是依赖于尚未使用的状态更新了。
I'm still kinda skeptical in general about the hooks API, but assuming this works I actually think it simplifies the code here. 我对钩子API总体上还是持怀疑态度的,但是假设此方法有效,我实际上认为它可以简化此处的代码。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.