useEffect продолжает повторную визуализацию компонента даже без обновления

#reactjs

#reactjs

Вопрос:

Пожалуйста, помогите, я новичок в react. Я пытаюсь создать простое приложение для обмена сообщениями.

Я хочу, чтобы компонент MessageForm слегка перерисовывался при обновлении [сообщений]. он делает это, но не останавливается, даже если обновления нет.

И я знаю это, потому что, когда я проверяю свои инструменты redux dev, я продолжаю видеть одно действие повторно

 const MessageForm = ({ getMessages, addMessages, deleteMessages, messages, roomId,}) => {
  useEffect(() => {
    getMessages(roomId);
    scrollToBottom();
  }, [messages]);

  const [formData, setFormData] = useState({
    text: "",
  });

  const { text } = formData;

  let messagesEnd;
  const scrollToBottom = () => {
    messagesEnd.scrollIntoView({ behavior: "smooth" });
  };

  const onChange = (e) =>
    setFormData({ ...formData, [e.target.name]: e.target.value });

  const onSubmit = (e) => {
    e.preventDefault();
    addMessages(text, roomId);
    setFormData({ text: "" });
  };

  let user = JSON.parse(localStorage.getItem("user"));
  let messageList = messages.messages.map((message) => {
    return message.user === user._id ? (
      <div className="row">
        <div className="container darker col-6" key={message._id}>
          <div className="username"> {message.name}</div>
          <Avatar
            size="50"
            round={true}
            src={message.avatar}
            className="right"
          />
          <p>{message.text.toString()}</p>
          <span className="time-left">
            {moments(message.createdAt).format("LLL")}
          </span>
          <span className="time-right">
            <Icon
              onClick={() => {
                deleteMessages(message._id);
              }}
              name="trash alternate"
              className="trashCan"
            />
          </span>
        </div>
      </div>
    ) : (
      <div className="row justify-content-end">
        <div className="container darker senders col-6 " key={message._id}>
          <div className="username"> {message.name}</div>
          <Avatar
            size="50"
            round={true}
            src={message.avatar}
            className="right"
          />
          <p>{message.text.toString()}</p>
          <span className="time-left">
            {moments(message.createdAt).format("LLL")}
          </span>
          <span className="time-right">
            <Icon
              onClick={() => {
                deleteMessages(message._id);
              }}
              name="trash alternate"
              className="trashCan"
            />
          </span>
        </div>
      </div>
    );
  });

  return (
    <div className="messageArea">
      <div className="textarea">
        {messageList}
        <div
          style={{ float: "left", clear: "both" }}
          ref={(el) => {
            messagesEnd = el;
          }}
        ></div>
      </div>

      <form className="row inputBox" onSubmit={onSubmit}>
        <div className="col-10">
          <input
            type="text"
            className="form-control"
            placeholder="Enter Message"
            aria-label="Enter Message"
            aria-describedby="basic-addon2"
            id="text"
            value={text}
            name="text"
            onChange={onChange}
          />
        </div>
        <div className="col-2">
          <button className="btn btn-outline-secondary" type="submit">
            Send
          </button>
        </div>
      </form>
    </div>
  );
};
const mapDispatchToProps = (dispatch) => ({
  addMessages: (text, roomId) => dispatch(addMessages(text, roomId)),
  getMessages: (roomId) => dispatch(getMessages(roomId)),
  deleteMessages: (id) => dispatch(deleteMessages(id)),
});

const mapStateToProps = (state, props) => ({
  messages: state.messages,
  roomId: props.location.state.roomId,
});

export default connect(mapStateToProps, mapDispatchToProps)(MessageForm);
  

Ответ №1:

Есть две проблемы.

  1. Вы вызываете getMessages метод, который, вероятно, обновляет messages оба реквизита, и если один из них изменится, обновится весь компонент. useEffect не контролирует родительский компонент, поэтому каждый раз, когда родительский компонент обновляется, дочерние элементы будут обновляться.
  2. Вторая проблема заключается в том, что вы вызываете метод, useEffect который перезагружается messages , затем он снова вызывается useEffect .

Я бы исправил это, getMessages выйдя из этого компонента, или я бы вставил оператор if, useEffect чтобы узнать, нужно ли вам вызывать getMessages .

Ответ №2:

Добавляя к ответу @nzajt, возможно, вам не нужно использовать «сообщения» в качестве зависимости от вашего useEffect. Просто рассматривайте его как componentDidMount

  useEffect(() => {
    getMessages(roomId);
    scrollToBottom();
  }, []);
  

Таким образом, он не будет перерисовываться бесконечно, но вы не получите больше обновлений после первого рендеринга.
Если вы ищете что-то в режиме реального времени, взгляните на что-нибудь с помощью websockets, настройте прослушиватели или, возможно, запустите функцию GetMessages каждые несколько секунд.