简体   繁体   English

useEffect 中的 state 指的是始终使用 React Hooks 的初始状态

[英]state inside useEffect refers the initial state always with React Hooks

Every time I emit a message from another component, I can't get the full list of messages.每次我从另一个组件发出消息时,我都无法获得完整的消息列表。 Here is the hook and view component:这是钩子和视图组件:

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 };
}

Unfortunately the state doesn't persist and shows always the last message:不幸的是,状态不会持续,并且总是显示最后一条消息:

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

If I do this the regular way, everything works as intended:如果我以常规方式执行此操作,则一切都会按预期进行:

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>
    );
  }
}

Since you have written your useEffect to execute on initial mount of component, it creates a closure which references the initial value of messages and even if the messages update, it will still refer to the same value on subsequent calls 由于您已经编写了useEffect以在组件的初始安装时执行,因此它创建了一个引用messages初始值的闭包,即使消息更新,它仍会在后续调用中引用相同的值

You should instead configure the useEffect to run on initial mount and messages change 您应该将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 };
} 

or else you could use the callback pattern to update state 否则你可以使用回调模式来更新状态

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 };
}

As you are writing socket handler inside the useEffect() with an empty array, So this effect will run only once when your component will mount for the first time.当您使用空数组在useEffect()内编写套接字处理程序时,因此该效果只会在您的组件第一次安装时运行一次。 The socket.on() function (or closure) will memorize the initial value of the messages and even if the messages gets change the socket.on() closure will still refer to its initial value. socket.on()函数(或闭包)将记住消息的初始值,即使消息发生更改, socket.on()闭包仍将引用其初始值。 Solution for this problem will be to register our messages to the dependency array of effect.这个问题的解决方案是将我们的消息注册到 effect 的依赖数组中。

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 }; 
}

Here a new problem you will encounter that each time messages get changed a new socket with "chat message" handler is created which may result unexpected and addition code to run multiple times.在这里,您会遇到一个新问题,每次更改消息时都会创建一个带有“聊天消息”处理程序的新套接字,这可能会导致意外和附加代码多次运行。 To solve that issue you will have to de-register the earlier handler.要解决该问题,您必须取消注册较早的处理程序。 And I'll recommend you to create socket only once (eg inside App.js) and pass it as a props.我会建议您只创建一次套接字(例如在 App.js 中)并将其作为道具传递。

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

useEffect(() => { 
socket.on("chat message", msg => {
  const newState = update(messages, { $push: [msg] });
  setMessages(newState);
});   
//De-register old handler   
 return function(){
socket.off("chat message")   } }, [messages]);

return { messages }; }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM