#reactjs
Вопрос:
В моем приложении для чата у меня возникают проблемы с отображением сообщений пользователей после нажатия кнопки «Отправить».
Я получаю все данные сообщений из API и сохраняю их в состоянии. Вот соответствующая часть.
Я упростил код, потому что думаю, что проблема в логике моего кода и в нем нет синтаксических ошибок.
const Chat = (props) => {
const [selectedRoom, setSelectedRoom] = useState({})
const [roomMessages, setRoomMessages] = useState([])
let roomMessagesData = []
function getUserRoomMessages() {
fetch("url").then((response) => {
return response.json();
}).then((requestData) => {
setRoomMessages(requestData.message_list)
});
};
function sendMessage(message) {
fetch("url").then((response) => {
return response.json();
}).then((requestData) => {
getUserRoomMessages()
});
}
function handleSendMessageButton() {
let currentMsg = document.querySelector("#textBox").value;
if (currentMsg != "") {
sendMessage(currentMsg)
roomMessagesData.push(
<div className="message receiver">{currentMsg}</div>
)
}
document.querySelector("#textBox").value = "";
}
useEffect(() => {
getUserRoomMessages()
}, [selectedRoom])
// Note that:
// typeof roomMessages !== 'undefined' will always be true,
// since initially, roomMessages = [], it is defined.
// Instead just check if roomMessages is not empty, to avoid this operation.
if (roomMessages.length > 0) {
const sortedMessages = roomMessages.sort(
(a,b)=> a.message_id > b.message_id ? 1 : -1
);
for (let i = 0; i<sortedMessages.length; i ) {
if (sortedMessages[i].creator_id == user_id) {
roomMessagesData.push(
<div className="message receiver">
{sortedMessages[i].message_text}
</div>
);
} else {
roomMessagesData.push(
<div className="message sender">
{sortedMessages[i].message_text}
</div>
);
}
};
}
return (
<div>
//some code
<div className="chatBox">{roomMessagesData}</div>
<div><input type="text" id="textBox"/></div>
<div>
<input
id="btn"
type="button"
value="Send message"
onClick={handleSendMessageButton}
/>
</div>
</div>
)
}
В этой текущей логике, когда я нажимаю кнопку отправить сообщение, сообщение успешно отправляется в API, но не отображается. Только после того, как я обновлю страницу, появится новое сообщение.
Все вызовы API завершены успешно. Моя идея заключается в том, что, когда пользователь отправляет новое сообщение, я получаю недавно обновленный список сообщений из API, которые затем должны быть повторно отрисованы reactjs
. Или я хочу подтолкнуть новый элемент roomMessagesData
так, чтобы после его изменения reactjs
следовало перезагрузить его элементы.
Вопрос в том, в чем проблема в моем коде и что я должен сделать, чтобы достичь того, что я описал выше.
Комментарии:
1.
typeof roomMessages !== 'undefined'
вам не нужно этого делать. достаточно простогоif (roomMessages)
. в JS if возвращает atrue
, в операторе if, если значение параметра НЕnull
равно,undefined
,0
(для чисел),false
(obv).2. Я не уверен, что полностью понял ваш код, и если именно поэтому сообщения не отображаются, но только переменные useState являются компонентом повторной передачи, обычных-нет.
Ответ №1:
Прямо сейчас вы определили свой список с помощью переменной let roomMessagesData = []
,в то время как для его определения вам нужно использовать состояние. позже , когда вы измените это состояние, ваш компонент будет обновлен с момента изменения состояния. другой способ-развернуть крючок useEffect, в котором вы будете проходить roomMessagesData
как вторая зависимость. Таким образом , когда этот массив изменится, ваш компонент будет повторно отправлен.
useEffect(() => {
const copyRoomMessagesData = [...roomMessagesData]
}, [roomMessagesData])
<div className="chatBox">
{copyRoomMessagesData}
</div>
Комментарии:
1. Поскольку он уже определился
roomMessages
как СОСТОЯНИЕ иroomMessagesData
зависит от этого состояния… поэтому вместо переходаroomMessagesData
в состояние… он должен просто добавитьroomMessages
в качестве «зависимой» переменной внутриuseEffect
. Ты так не думаешь?2. Это тоже не сработает, так как вы будете попадать в крючок useEffect с каждым изменением, но это не меняет того факта, что вы
roomMessagesData
не будете перенаправлены.3. Помните, что если
roomMessages
что-то меняется, значитroomMessagesData
, меняется… ты согласен? И если да, то в случаеroomMessages
измененийuseEffect
будет выполняться повторная визуализация компонента.. согласны? ИroomMessages
изменения происходят только после того, как ahandleSendMessageButton
запускается изнутриInput
… Он используетonClick
неonChange
… кстати!! На самом деле, он должен перенести этоonClick
сInput
button
» в » на «а».4. @MwamiTovi Ну, он должен многое изменить в своем коде, но давайте придерживаться реальной цели здесь. во-первых, я изучил то, что предложил U, но разве это не приводит к бесконечному циклу? В том, что вы предложили, мы в основном устанавливаем состояние внутри крючка useEffect, который является observingit ( не устанавливая его прямо внутри него, а вызывая функцию, которая это делает ). во-вторых, я не согласен с тем, что вы сказали о повторной визуализации компонента. Возможно, я немного ошибаюсь, но я думаю, что будет повторно отображена только та часть нашего компонента, которую мы вызываем внутри крючка useEffect.
5. На самом деле я понимаю вашу точку зрения, потому
getUserRoomMessages
что обновляетroomMessages
состояние, и оно называется внутриuseEffect
… да, я предвижу бесконечный цикл. Это мост, который мы можем пересечь, если ему нужно, чтобы мы пошли туда… он должен удалить этотgetUserRoomMessages
вызов, потому что, помимо создания бесконечного цикла, он также запускает бесконечные вызовы API! Но цель, которую он описывает, для повторного отображения компонента возможна только путем добавления правильного «зависимого» состояния/переменных вuseEffect
.
Ответ №2:
Краткий обзор шагов:
- Таким
handleSendMessageButton
образом, запускаетсяsendMessage
функция, которая выполняет aPOST
. - А затем
sendMessage
запускаетGET
сообщение с помощьюgetUserRoomMessages
. - Внутри
getUserRoomMessages
вы запускаете обновление состояния с помощьюsetRoomMessages
. - И отображаемый ребенок
{roomMessagesData}
зависит от изменений вroomMessages
состоянии.
Исходя из вышесказанного, проблема, которую необходимо устранить, находится здесь:
// ONLY re-renders if selectedRoom changes
useEffect(() => { getUserRoomMessages() }, [selectedRoom]);
Измените вышеизложенное на:
// re-render if roomMessages changes,
// and the message should display automatically
useEffect(() => { getUserRoomMessages() }, [roomMessages]);
Помните, что useEffect()
это повторится только в том случае, если изменятся определенные «зависимые» состояния.
Обновить
Несмотря на предложение выше, обратите внимание, что он создаст infinite loop
, потому что — getUserRoomMessages
вызовет вызов API, а затем обновит roomMessages
состояние, от которого useEffect
зависит повторная визуализация компонента, что приведет к бесконечной повторной визуализации и многочисленным вызовам API.
Предложение:
Так getUserRoomMessages
как уже вызывается при onClick
срабатывании, таким образом, дата обновления… вместо этого вы можете использовать эффект использования для запуска только при обновлении состояния, например
let loaded = React.useRef(false);
// Cleanup useEffect
useEffect(() => {
// set to true on mount...
isLoaded.current = true;
// ... and to false on unmount
return () => { isLoaded.current = false; };
}, [isLoaded, roomMessages]);