![](/img/trans.png)
[英]How to conditionally change state in useEffect after a delay in React?
[英]How to iterate over an state array in React and conditionally delay the next iteration?
項目:我正在構建一個聊天機器人,我想按順序呈現聊天機器人的消息,彼此之間有延遲。
問題:消息保存在 state 數組中。 到目前為止,我在ChatScreen組件的 JSX 中使用map
來返回包含實際消息的ChatBotMessage組件。 由於map
不支持異步行為(除了用Pomise.all()
解決所有承諾,這對我的情況沒有幫助),我嘗試使用for..of
循環。 這里的問題是我無法在循環中返回ChatBotMessage組件,因為一旦我返回某些內容,循環就會停止。
想要的行為:我需要找到一種方法來遍歷 state 數組並在 JSX 中延遲顯示最新消息。 state object 看起來像這樣:
messages = [
{type: text,
text: 'Chatbot message'
},
{type: 'typing',
value: true
},
{type: text,
text: 'Chatbot message'
},
{type: 'typing',
value: true
}
....
]
每次迭代遇到一個設置為 true 的 type = 'typing' 元素時,它應該延遲下一條消息。 我怎樣才能做到這一點?
編輯:添加了ChatScreen組件:
export const ChatScreen = ({ navigation }) => {
const [input, setInput] = useState("");
const dispatch = useDispatch();
const messageState: MessagesState = useSelector((state: RootState) => state.messagesState)
const userState: User = useSelector((state: RootState) => state.userState)
const scrollToRef: any = useRef();
const scrollToBottom = () => {
if (scrollToRef.current) {
scrollToRef.current.scrollToEnd({ animated: true })
}
}
useEffect(() => {
if (!messageState.loadedHistory && !messageState.updatingHistory) {
dispatch(requestMessagesHistory(userState.id))
}
}, []);
useEffect(() => {
const onFocus = navigation.addListener('focus', () => {
if (!messageState.loaded && !messageState.updating && messageState.loadedHistory && !messageState.updatingHistory) {
dispatch(requestMessagesInitial(userState.id));
}
});
return onFocus;
}, [navigation]);
return (
<View style={styles.container}>
<ScrollView
ref={scrollToRef}
onContentSizeChange={() => {
if (scrollToRef.current) {
scrollToRef.current.scrollToEnd({ animated: true })
}
}
style={styles.containerInner}>
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={styles.chatArea}>
{*Here I need to iterate over the message array and return the Chat Messages*}
{messageState.messages.map((item, index, arr) => {
if (item.type === 'text' && item.text !== 'init') {
if (!item.human) {
return <ChatMessageBot index={index} text={item.text} />
} else if (item.human) {
return <ChatMessageHuman index={index} message={item.text} />
} else {
return <ChatMessageBot index={index} text={item.text} />;
}
}
})}
</View>
</TouchableWithoutFeedback>
</ScrollView>
</View>
)
}
}
你在 for 循環的正確軌道上。 正如您所說, promise.all
並不是真正有用,因為您需要按順序鏈接異步等待。
您可以使用async
await
語法相對巧妙地解決這個問題。
嘗試這樣的事情:
(顯然這段代碼不是反應代碼,但你的問題比反應更根本)
// Stub, replace with whatever is appropriate (e.g. React state update, or prop invocation)
const renderMessage = (message) => console.log('Message rendered: ', message);
const executeMessages = async (messages, delay) => {
for(message of messages) {
if (message.type === 'text') {
renderMessage(message.value);
} else if (message.type ==="typing" && message.value===true) {
await new Promise(resolve => setTimeout(resolve, typingDelay)); // this creates a promise which will resolve after the typingDelay has elapsed
}
}
}
executeMessages([
{ type: "text", text: "Chatbot message" },
{ type: "typing", value: true },
{ type: "text", text: "Chatbot message" },
{ type: "typing", value: true },
], 3000); // 3000 is 3 seconds
我做出這個答案是為了給我的評論提供更多背景信息,我將在此重復:
我可能沒有按照您的意思解釋您的問題,但我認為如果您預處理數組以向消息對象添加delay: {number}
字段,然后您使呈現每條消息的組件尊重內部延遲,這可能會解決您的問題。
假設我正確解釋了您的評論,您可以預處理數組以將delay
屬性添加到正確的messages
中:
messages = [
{type: text,
text: 'Chatbot message'
},
{type: 'typing',
value: true
},
{type: text,
text: 'Chatbot message'
},
{type: 'typing',
value: true
},
{type: text,
text: 'final message',
delay: 2 // seconds
},
]
然后,我假設您的 JSX 類似於
messages.map(message => <ChatBotMessage message={message} />)
因此,在您的 ChatBotMessage 中,您創建了一些 state,可能delayed = true
,然后超時 function 將采用您的delay
值, this.props.message.delay || 0
this.props.message.delay || 0
,並在 settimeout 回調中設置 state delayed = false
。
然后在您的渲染 function 中,如果延遲為真,則返回null
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.