state внутри useEffect всегда ссылается на начальное состояние с помощью React Hooks

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

export function useChat() {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    const socket = openSocket("http://localhost:3003");
    socket.on("chat message", msg => {
      const newState = update(messages, { $push: [msg] });
      setMessages(newState);
    });
  }, []);

  return { messages };
}

К сожалению, состояние не сохраняется и всегда показывает последнее сообщение:

export const HookSockets = () => {
  const { messages } = useChat();
  return (
    <div>
      {messages.map((message, index) => (
        <div key={index}>{message}</div>
      ))}
    </div>
  );
};

Если я сделаю это обычным способом, все будет работать по назначению:

export class ClassSockets extends Component {
  state = {
    socket: openSocket("http://localhost:3003"),
    messages: [],
    message: ""
  };

  componentDidMount() {
    this.state.socket.on("chat message", msg => {
      const newState = update(this.state, {
        messages: { $push: [msg] }
      });
      this.setState(newState);
    });
  }

  handleClick = () => {
    this.state.socket.emit("chat message", this.state.message);
    this.setState({ message: "" });
  };

  handleChange = event => {
    this.setState({ message: event.target.value });
  };

  render() {
    return (
      <div>
        <div>Sockets</div>
        <div>{this.state.messages}</div>
        <input
          type="text"
          value={this.state.message}
          onChange={this.handleChange}
        />
        <button onClick={this.handleClick}>Send Message</button>
      </div>
    );
  }
}

person mikes    schedule 13.02.2019    source источник


Ответы (1)


Поскольку вы написали свой useEffect для выполнения при начальном монтировании компонента, он создает замыкание, которое ссылается на начальное значение messages, и даже если сообщения обновляются, он все равно будет ссылаться на то же значение при последующих вызовах

Вместо этого вы должны настроить useEffect для запуска при первоначальном монтировании и изменении сообщений

export function useChat() {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    const socket = openSocket("http://localhost:3003");
    socket.on("chat message", msg => {
      const newState = update(messages, { $push: [msg] });
      setMessages(newState);
    });
  }, [messages]);

  return { messages };
} 

иначе вы можете использовать шаблон обратного вызова для обновления состояния

export function useChat() {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    const socket = openSocket("http://localhost:3003");
    socket.on("chat message", msg => {
      setMessages(prevMessages => update(prevMessages, { $push: [msg] }););
    });
  }, []);

  return { messages };
}
person Shubham Khatri    schedule 14.02.2019