useEffect 中的状态总是使用 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()
闭包仍将引用其初始值。这个问题的解决方案是将我们的消息注册到效果的依赖数组。
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 }; }
每次我从另一个组件发出消息时,都无法获取完整的消息列表。这是挂钩和视图组件:
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()
闭包仍将引用其初始值。这个问题的解决方案是将我们的消息注册到效果的依赖数组。
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 }; }