簡體   English   中英

如何在 React 中迭代 state 數組並有條件地延遲下一次迭代?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM