Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Right way to fetch data with react using socket.io

I didn't found any examples about how to fetch data from express server using react with socket.io.

Now i do something like this: 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)
    }
  }, [])
like image 995
ZiiMakc Avatar asked Nov 19 '18 22:11

ZiiMakc


People also ask

Can you use socket IO With React?

Navigate into the client folder via your terminal and create a new React. js project. Install Socket.io client API and React Router. React Router is a JavaScript library that enables us to navigate between pages in a React application.

Where should you fetch data in React?

Considerations before fetching data When we request data, we must prepare a state to store the data upon return. We can store it in a state management tool like Redux or store it in a context object. But, to keep things simple, we will store the returned data in the React local state.

How fetch data from React to backend?

The most accessible way to fetch data with React is using the Fetch API. The Fetch API is a tool that's built into most modern browsers on the window object ( window. fetch ) and enables us to make HTTP requests very easily using JavaScript promises.


2 Answers

This looks fine, but there are some things you can improve on, such as disconnecting the socket before unmounting and not making the socket part of state (refer to the code example below).

If you're confused over how to port existing code to hooks, write out the component using classes first, then port part by part to hooks. You could refer to this StackOverflow answer as a cheatsheet.

Using traditional classes, using socket.io looks like:

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() {
    ...
  }
}

Then you can port the this.socket to use useRef (it doesn't need to be part of state as your render() function doesn't need it. So useRef is a better alternative (although useState is likely to still work).

Port componentDidMount() via using useEffect and passing an empty array as the second argument to make the effect callback only run on mount.

Port componentWillUnmount() via returning a callback function in the useEffect callback which React will call before unmounting.

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 ...;
}
like image 183
Yangshun Tay Avatar answered Oct 02 '22 17:10

Yangshun Tay


The accepted answer has the downside, that the initial state of the useRef() gets called on every re-render. With a text input for example, a new connection is established on every input change. I came up with two solutions:

  1. Define the socket in the useEffect
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>
  );
};
  1. Define the socket.io in a useState()
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;
like image 44
Fabian Avatar answered Oct 02 '22 17:10

Fabian