使用 socket.io 通过 React 获取数据的正确方法
Right way to fetch data with react using socket.io
我没有找到任何关于如何使用 react with socket.io 从快速服务器获取数据的示例。
现在我做这样的事情:
Server.js
io.on('connection', socket => {
console.log(socket.id)
socket.on('disconnect', () => {
console.log(socket.id + ' disconnected')
})
socket.on('load settings', () => {
socket.emit('settings is here', data)
})
})
React.js
const [socket] = useState(io())
const [settings, setSettings] = useState(false)
useEffect(() => {
try {
socket.emit('load settings');
socket.on('settings is here', (data) => {
// we get settings data and can do something with it
setSettings(data)
})
} catch (error) {
console.log(error)
}
}, [])
这看起来不错,但有些地方您可以改进,例如在卸载之前断开套接字,并且不使套接字成为状态的一部分(请参阅下面的代码示例)。
如果您对如何将现有代码移植到挂钩感到困惑,请先使用 类 编写组件,然后逐个移植到挂钩。您可以将此 称为备忘单。
使用传统的 类,使用 socket.io 看起来像:
class App extends React.Component {
constructor(props) {
super(props);
this.socket = io();
}
componentDidMount() {
this.socket.open();
this.socket.emit('load settings');
this.socket.on('settings is here', (data) => {
// we get settings data and can do something with it
this.setState({
settings: data,
})
});
}
componentWillUnmount() {
this.socket.close();
}
render() {
...
}
}
然后您可以移植 this.socket
以使用 useRef
(它不需要成为 state
的一部分,因为您的 render()
函数不需要它. 所以 useRef
是一个更好的选择(尽管 useState
可能仍然有效)。
Port componentDidMount()
通过使用 useEffect
并传递一个空数组作为第二个参数,使效果回调仅 运行 挂载。
Port componentWillUnmount()
通过在 useEffect
回调中返回一个回调函数,React 将在卸载前调用该回调函数。
function App() {
const socketRef = useRef(null);
const [settings, setSettings] = useState(false);
useEffect(() => {
if (socketRef.current == null) {
socketRef.current = io();
}
const {current: socket} = socketRef;
try {
socket.open();
socket.emit('load settings');
socket.on('settings is here', (data) => {
// we get settings data and can do something with it
setSettings(data);
})
} catch (error) {
console.log(error);
}
// Return a callback to be run before unmount-ing.
return () => {
socket.close();
};
}, []); // Pass in an empty array to only run on mount.
return ...;
}
接受的答案有一个缺点,即 useRef() 的初始状态在每次重新渲染时都会被调用。例如,对于文本输入,每次输入更改时都会建立一个新连接。我想出了两个解决方案:
- 在useEffect中定义socket
const ChatInput = () => {
const [chatMessage, setChatMessage] = useState<string>('');
const socket = useRef<Socket>();
useEffect(() => {
socket.current = io('my api');
socket.current.on('chat message', (message: string) => {
setChatMessage(message);
});
return () => { socket.current?.disconnect(); };
}, []);
const inputHandler = (text: string) => {
socket.current?.emit('chat message', text);
};
return (
<View>
<Text>{chatMessage}</Text>
<TextInput onChangeText={(text) => inputHandler(text)} />
</View>
);
};
- 在 useState()
中定义 socket.io
const ChatInput = () => {
const [chatMessage, setChatMessage] = useState<string>('');
const [socket] = useState(() => io('my api'));
useEffect(() => {
socket.on('chat message', (message: string) => {
setChatMessage(message);
});
return () => { socket.disconnect(); };
}, []);
const inputHandler = (text: string) => {
socket.emit('chat message', text);
};
return (
<View>
<Text>{chatMessage}</Text>
<TextInput onChangeText={(text) => inputHandler(text)}/>
</View>
);
};
export default ChatInput;
我没有找到任何关于如何使用 react with socket.io 从快速服务器获取数据的示例。
现在我做这样的事情: Server.js
io.on('connection', socket => {
console.log(socket.id)
socket.on('disconnect', () => {
console.log(socket.id + ' disconnected')
})
socket.on('load settings', () => {
socket.emit('settings is here', data)
})
})
React.js
const [socket] = useState(io())
const [settings, setSettings] = useState(false)
useEffect(() => {
try {
socket.emit('load settings');
socket.on('settings is here', (data) => {
// we get settings data and can do something with it
setSettings(data)
})
} catch (error) {
console.log(error)
}
}, [])
这看起来不错,但有些地方您可以改进,例如在卸载之前断开套接字,并且不使套接字成为状态的一部分(请参阅下面的代码示例)。
如果您对如何将现有代码移植到挂钩感到困惑,请先使用 类 编写组件,然后逐个移植到挂钩。您可以将此
使用传统的 类,使用 socket.io 看起来像:
class App extends React.Component {
constructor(props) {
super(props);
this.socket = io();
}
componentDidMount() {
this.socket.open();
this.socket.emit('load settings');
this.socket.on('settings is here', (data) => {
// we get settings data and can do something with it
this.setState({
settings: data,
})
});
}
componentWillUnmount() {
this.socket.close();
}
render() {
...
}
}
然后您可以移植 this.socket
以使用 useRef
(它不需要成为 state
的一部分,因为您的 render()
函数不需要它. 所以 useRef
是一个更好的选择(尽管 useState
可能仍然有效)。
Port componentDidMount()
通过使用 useEffect
并传递一个空数组作为第二个参数,使效果回调仅 运行 挂载。
Port componentWillUnmount()
通过在 useEffect
回调中返回一个回调函数,React 将在卸载前调用该回调函数。
function App() {
const socketRef = useRef(null);
const [settings, setSettings] = useState(false);
useEffect(() => {
if (socketRef.current == null) {
socketRef.current = io();
}
const {current: socket} = socketRef;
try {
socket.open();
socket.emit('load settings');
socket.on('settings is here', (data) => {
// we get settings data and can do something with it
setSettings(data);
})
} catch (error) {
console.log(error);
}
// Return a callback to be run before unmount-ing.
return () => {
socket.close();
};
}, []); // Pass in an empty array to only run on mount.
return ...;
}
接受的答案有一个缺点,即 useRef() 的初始状态在每次重新渲染时都会被调用。例如,对于文本输入,每次输入更改时都会建立一个新连接。我想出了两个解决方案:
- 在useEffect中定义socket
const ChatInput = () => {
const [chatMessage, setChatMessage] = useState<string>('');
const socket = useRef<Socket>();
useEffect(() => {
socket.current = io('my api');
socket.current.on('chat message', (message: string) => {
setChatMessage(message);
});
return () => { socket.current?.disconnect(); };
}, []);
const inputHandler = (text: string) => {
socket.current?.emit('chat message', text);
};
return (
<View>
<Text>{chatMessage}</Text>
<TextInput onChangeText={(text) => inputHandler(text)} />
</View>
);
};
- 在 useState() 中定义 socket.io
const ChatInput = () => {
const [chatMessage, setChatMessage] = useState<string>('');
const [socket] = useState(() => io('my api'));
useEffect(() => {
socket.on('chat message', (message: string) => {
setChatMessage(message);
});
return () => { socket.disconnect(); };
}, []);
const inputHandler = (text: string) => {
socket.emit('chat message', text);
};
return (
<View>
<Text>{chatMessage}</Text>
<TextInput onChangeText={(text) => inputHandler(text)}/>
</View>
);
};
export default ChatInput;