繁体   English   中英

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

[英]state inside useEffect refers the initial state always with 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>
    );
  }
}

由于您已经编写了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 };
}

当您使用空数组在useEffect()内编写套接字处理程序时,因此该效果只会在您的组件第一次安装时运行一次。 socket.on()函数(或闭包)将记住消息的初始值,即使消息发生更改, socket.on()闭包仍将引用其初始值。 这个问题的解决方案是将我们的消息注册到 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 }; 
}

在这里,您会遇到一个新问题,每次更改消息时都会创建一个带有“聊天消息”处理程序的新套接字,这可能会导致意外和附加代码多次运行。 要解决该问题,您必须取消注册较早的处理程序。 我会建议您只创建一次套接字(例如在 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