Reactjs 无限循环或useEffect中缺少依赖项

Reactjs 无限循环或useEffect中缺少依赖项,reactjs,react-hooks,Reactjs,React Hooks,每次有新状态时,我都要在前一个状态上应用一个函数。 用户案例:用户建立到服务器的连接。我创建了一个WebSocket对象并将其放入webSocketInstance。当用户输入他的usename时,我会将该名称添加到Redux中,这会导致重新呈现组件,这是正常的。现在,由于他有了用户名,我希望他重新连接一个新的url(这也是重新连接逻辑的一部分)。所以我希望他关闭以前的连接并打开一个新的连接 export default function GameKeeper() { // Redux st

每次有新状态时,我都要在前一个状态上应用一个函数。 用户案例:用户建立到服务器的连接。我创建了一个WebSocket对象并将其放入webSocketInstance。当用户输入他的usename时,我会将该名称添加到Redux中,这会导致重新呈现组件,这是正常的。现在,由于他有了用户名,我希望他重新连接一个新的url(这也是重新连接逻辑的一部分)。所以我希望他关闭以前的连接并打开一个新的连接

export default function GameKeeper() {
  // Redux states here
  const { adminWsUrl, clientWsUrl, clientCode, playerName } = useSelector(
    (state: RootState) => state.hosting
  );
  const [webSocketInstance, setWebSocketInstance] = useState<WebSocket>();
  
  useEffect(() => {
    if (webSocketInstance) {
      webSocketInstance.close();
    }
    if (adminWsUrl) {
      setWebSocketInstance(new WebSocket(adminWsUrl));
    } else if (clientWsUrl && playerName) {
      setWebSocketInstance(new WebSocket(`${clientWsUrl}${playerName}/`));
    } else if (clientWsUrl && !playerName) {
      setWebSocketInstance(new WebSocket(clientWsUrl));
    }
  }, [adminWsUrl, clientWsUrl, playerName]);
  
  
 return (
  ...
 );
 
}
导出默认函数GameKeeper(){
//这里是Redux州
const{adminWsUrl,clientWsUrl,clientCode,playerName}=useSelector(
(state:RootState)=>state.hosting
);
const[webSocketInstance,setWebSocketInstance]=useState();
useffect(()=>{
如果(webSocketInstance){
webSocketInstance.close();
}
if(adminWsUrl){
setWebSocketInstance(新WebSocket(adminWsUrl));
}else if(clientWsUrl&&playerName){
setWebSocketInstance(新的WebSocket(`${clientWsUrl}${playerName}/`));
}else if(clientWsUrl&!playerName){
setWebSocketInstance(新WebSocket(clientWsUrl));
}
},[adminWsUrl、clientWsUrl、playerName]);
返回(
...
);
}

当前代码警告我webSocketInstance不在useEffect依赖项中。如果我添加它,我会得到无限循环。

这里有一个基本的模板,我将如何设置一个
useReducer来处理这个问题。我真的想不出一个好办法,只要使用
useState
,而不使用一些变异状态(即
useRef
)来保存Web套接字。当然,这里的缺点是您已经完全脱离了React的状态系统,无法再监视套接字的更新

function wsReducer(wsInstance, action) {
  if (action.type === "reset") {
    wsInstance && wsInstance.close();
    return new WebSocket(action.payload);
  }
}

export default function App() {
  const { adminWsUrl, clientWsUrl, clientCode, playerName } = useSelector(
    (state: RootState) => state.hosting
  );
  const [wsInstance, dispatch] = useReducer(wsReducer, null);

  useEffect(() => {
    const url = (() => {
      if (adminWsUrl) return adminWsUrl;
      if (clientWsUrl && playerName) return `${clientWsUrl}${playerName}/`;
      if (clientWsUrl && !playerName) return clientWsUrl;
      return false;
    })();

    if (url) {
      dispatch({ type: "reset", payload: url });
    }
  }, [adminWsUrl, clientWsUrl, playerName]);

  return (
    ...
  );
}

此外,还有一些——特别是关于解耦更新/操作以及在本文中使用
useReducer
的内容。我建议尝试完成整个过程,当我开始做functional React时,它对我非常有用。

省略号让我紧张-我建议发布整个
useEffect
。这可能是一种将数据流拆分为更多状态和效果的情况,这样您就可以正确地解耦所有内容。此外,您可能会受益于
useReducer
方法,而不是
useState
。。。还有什么可能触发此效果运行。@lawrence witt,DrewReese“当您有涉及多个子值的复杂状态逻辑或下一个状态依赖于前一个状态时,useReducer通常比useState更可取。”这来自reactjs.org,似乎正是我需要的,Thankstree,reducer函数处理更复杂的状态更新,但是
useState
也有一个函数更新,基本上减去“action”。在这两种情况下,它们都是纯功能的,没有副作用。我认为这个问题更多的是关于打开/关闭套接字的影响,而不是更新状态。我在SO和套接字上遇到了一些类似的问题,最终的解决方案通常涉及不将套接字存储在状态,因为似乎总是存在循环问题。您是否尝试过使用react ref或全局变量(在组件定义的文件中)来保存套接字实例?