У меня есть простой компонент на основе классов, который я пытаюсь преобразовать в компонент на основе функций, но я захожу в тупики.
Мой компонент представляет собой прямую адаптацию стандартного пакета gifted-chat
и использует Watson Assistant в качестве бэкэнда для предоставления ответов. В бэкэнд-части нет ничего сложного, это всего лишь тонкие обертки API Watson Assistants:
getSessionID = async (): Promise<string>
получает идентификатор сеанса для использования при взаимодействии с серверной частью, и
sendReply = async (reply: string, sessionID: string): Promise<string>
возвращает ответ Ассистента на строку, представленную как reply
. Это не источник моей проблемы (тела обоих можно заменить на return await "some string"
, и у меня возникнут те же проблемы): версия на основе классов (ниже) работает отлично.
Но я не понимаю, как преобразовать это в функциональную форму, в частности:
- Я изо всех сил пытаюсь найти подходящую замену для
componentWillMount
. ИспользованиеuseEffect
сsessionID
в качестве состояния приводит к ошибкам: вызываетсяgetMessage
(даже если яawait
) до того, как будет установлен требуемый идентификатор сеанса.
Я могу избежать этого, не создавая состояние sessionID (которого, возможно, не должно быть), а просто сделав его глобальным (как в функциональной попытке ниже). Но даже если я сделаю это:
- После каждого ответа пользователя и получения ответа ответ пользователя удаляется из беседы, так что вся беседа состоит только из сгенерированных ответов.
Обе эти проблемы, я думаю, связаны с отсутствием обратных вызовов в идиоме установки состояния на основе хуков, но проблема также может заключаться в другом. В любом случае, я не знаю, что мне делать.
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 (неудачная попытка на основе функции)
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 (версия на основе рабочих функций)
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
sessionID
в состояние (как в классе на основе), а затем прослушайте это изменение с помощью другого useEffect, чтобы затем вызватьgetMessage
. - person Alvaro   schedule 25.08.2019react-usestate-callback
. Есть ли причина избегать этого? - person orome   schedule 25.08.2019useState
иuseEffect(foobar, [myStateValue])
, как вы можете видеть в исходном коде библиотеки: github.com/the-road-to-learn-react/use-state-with-callback/blob/ - person Victor Levasseur   schedule 25.08.2019