useEffect 中的 state 总是使用 React Hooks 引用初始状态
Posted
技术标签:
【中文标题】useEffect 中的 state 总是使用 React Hooks 引用初始状态【英文标题】:state inside useEffect refers the initial state always with React Hooks 【发布时间】:2019-07-07 14:26:15 【问题描述】:每次我从另一个组件发出消息时,我都无法获得完整的消息列表。这是钩子和视图组件:
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>
);
【问题讨论】:
【参考方案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 ;
【讨论】:
【参考方案2】:当您在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 ;
【讨论】:
以上是关于useEffect 中的 state 总是使用 React Hooks 引用初始状态的主要内容,如果未能解决你的问题,请参考以下文章
使用 useEffect 更新 useReducer 的“状态”
为啥Android中的文件'/sys/power/state'总是'mem'?
React useEffect 无限循环获取数据 axios