2
//App.tsx
import './App.css';
import { Canvas } from '@react-three/fiber';
import { Player } from './components/Player';
import { TCharacter } from './types';
import { Chat } from './components/Chat';
import { NetworkProvider } from './network/useSocket';
import { GUI, InnerGUI } from './components/GUI';
function App(props: { character?: TCharacter }) {
    const { character = 'butterfly' } = props
    return (
        <div id='canvas'>
            <NetworkProvider>
                <Canvas>
                    <ambientLight />
                    <Player character={character} position={[0, 1, 0]} speed={4} />
                    <InnerGUI />
                </Canvas>
                <Chat />
                <GUI />
            </NetworkProvider>
        </div>
    );
}

export default App;

In the code above, All 4 components(Chat, GUI, Player, InnerGUI) use a single socket that is created only one time in whole program.


import { useSocket } from "../network/useSocket"

const GUI = () => {
    const { socket } = useSocket()

    useEffect(() => {
        console.log(`GUI SK: `, socket)
    }, [socket, socket?.connected])

    return <>
        <input type='button' value='GUI' style={{ position: 'fixed', bottom: 0, right: 0 }} />
    </>
}

const InnerGUI = () => {
    const { socket } = useSocket()

    useEffect(() => {
        console.log(`innerGUI SK: `, socket)
    }, [socket, socket?.connected])

    return <Html>
        <input type='button' value='INNER GUI' />
    </Html>
}

const Chat = () => {
    const { socket } = useSocket()

    useEffect(()=>{
        console.log(`chatSK: `, socket)
    },[socket, socket?.connected])
...

const Player = (props: { character: 'bee' | 'butterfly', position: number[], speed?: number }) => {
    const { position, speed = 1 } = props
    const keyStat: KeyStat = useKeyboard()
    const [action, setAction] = useState('idle')
    const ref = useRef<THREE.Mesh>(null!)
    const modelRef = useRef<THREE.Mesh>(null!)
    const camRef = useRef<PerspectiveCameraImpl>(null!)
    const { socket } = useSocket()

    useEffect(()=>{
        console.log(`PlayerSK: `, socket)
    },[socket, socket?.connected])
...

But when I execute this code, components outside uses useSocket hook well, but components inside doesn't. When I print the socket that components have got, inside and outside are not the same.

Here's the output enter image description here

React.StrictMode is off now. When React.StrictMode is on, socket is initiated twice.

And Here's the useSocket Code

import { createContext, useContext, useEffect, useState } from 'react'
import { io, Socket } from 'socket.io-client'
import { IChat, IPlayerInfo } from '../types';

declare global {
    interface Window {
        chat: (msg: string) => void
    }
}
interface ISocketData {
    timestamp: number,
    payload: any
}

const NetworkContext = createContext<{ socket: Socket, playerList: { [key: string]: IPlayerInfo }, chatData: IChat | null }>({ socket: io(''), playerList: {}, chatData: null });
const NetworkProvider = ({ children }: { children: any }) => {
    const [socket, setSocket] = useState<Socket>(null!)
    const [socketConnected, setSocketConnected] = useState(false)
    const [timestamp, setTimestamp] = useState(new Date().getTime())
    const [playerList, setPlayerList] = useState<{ [key: string]: IPlayerInfo }>({})
    const [chatData, setChatData] = useState<IChat | null>(null)
    useEffect(() => {
        setSocket(io(`localhost:2002`))
    }, [])

    useEffect(() => {
        if (!socket) return;

        socket.on('connect', () => {
            console.log('socket connected')
            setSocketConnected(socket.connected);
        });
        socket.on('disconnect', () => {
            console.log('socket disconnected')
            setSocketConnected(socket.connected);
        });

        socket.on('update', (data: ISocketData) => {
            if (data.timestamp < timestamp) return
            setPlayerList(data.payload)
            setTimestamp(data.timestamp)
        })

        socket.on('chat', (data: IChat) => {
            setChatData(data)
        })
    }, [socket]);

    return <NetworkContext.Provider value={{ socket, playerList, chatData }} children={children} />
}

const useSocket = () => {
    const { socket, playerList, chatData } = useContext(NetworkContext)
    return { socket, playerList, chatData }
}

export { NetworkProvider, useSocket }

0 Answers0