React - использовать крючок эффекта - componentDidMount для использования эффекта
Я хотел бы преобразовать это в useEffect
крюк:
КОД
componentDidMount () {
this.messagesRef.on('child_added', snapshot => {
const message = snapshot.val();
message.key = snapshot.key;
this.setState({messages: this.state.messages.concat(message
)});
});
ОБНОВЛЕННЫЙ КОД
const MessageList = () => {
const [messages, setMessage] = useState([]);
const [usernames, setUsernames] = useState('');
const [contents, setContents] = useState('');
const [roomId, setRoomId] = useState('');
const messagesRef = MessageList.props.firebase.database().ref('messages');
useEffect(() => {
messagesRef.on('child added', snapshot => {
const message = snapshot.val();
message.key = snapshot.key;
const [messages, setMessages] = useState({messages: messages.concat(message)});
});
})
}
Прямо сейчас это дает мне useState cannot be used in a callback
,
Как я могу решить это или преобразовать это правильно?
3 ответа
Там есть пара вещей. Во-первых, чтобы исправить код, вы можете обновить useEffect
к этому:
useEffect(() => {
messagesRef.on('child added', snapshot => {
const message = snapshot.val();
message.key = snapshot.key;
setMessages(messages.concat(message)); // See Note 1
}, []); // See Note 2
Примечание 1
setMessages
линия, как вы обновляете свое состояние. useState
немного отличается от "старого" setState
в том смысле, что полностью заменит государственную стоимость. Реактивная документация гласит:
Это потому, что когда мы обновляем переменную состояния, мы заменяем ее значение. Это отличается от this.setState в классе, который объединяет обновленные поля в объект.
Заметка 2
React Hooks меняет способ создания приложений, и это не настоящий "перевод" из старых жизненных циклов.
Пустые скобки ([]
) в последней строке сделает ваш код "похожим" на componentDidMount
, но самое главное, заставит ваш эффект работать только один раз.
Дан Абрамов сказал (убрал часть оригинального текста):
Хотя вы можете использовать Effect(fn, []), он не является точным эквивалентом. В отличие от componentDidMount, он будет захватывать реквизиты и состояния. Так что даже внутри обратных вызовов вы увидите начальный реквизит и состояние. (...) Имейте в виду, что ментальная модель эффектов отличается от componentDidMount и других жизненных циклов, и попытка найти их точные эквиваленты может сбить вас с толку больше, чем помочь. Чтобы стать продуктивным, вам нужно "мыслить эффектами", и их ментальная модель ближе к реализации синхронизации, чем к реагированию на события жизненного цикла.
Полная статья об использовании эффекта здесь.
Вы попытались объявить состояние снова вместо использования средства обновления состояния
useEffect(() => {
messagesRef.on('child added', snapshot => {
const message = snapshot.val();
message.key = snapshot.key;
// setMessages is the state updater for messages
// instead of an object with messages: messagesArray
// just save it as an array the name is already messages
setMessages([...messages, message]);
});
// useEffect takes an array as second argument with the dependencies
// of the effect, if one of the dependencies changes the effect will rerun
// provide an empty array if you want to run this effect only on mount
}, []);
Я нашел альтернативное решение, которое не требует модификации html. Мы создаем компонент более высокого порядка, который отображает некоторый ожидающий элемент и переключает состояние в componentDidMount или использует эффект для рендеринга целевого компонента.
import React, { useEffect, useState } from 'react';
const Loading = (props) => {
const [loading, setLoading] = useState(true);
useEffect(() => {
setLoading(false);
}, []);
return (
<>
{loading ?
<div style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
width: '100%',
height: '100%',
fontSize: '5vh'
}}>
Loading...
</div> :
props.children}
</>
);
};
экспорт по умолчанию Загрузка;
Недостаток в том, что не работают анимированные элементы.