最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Right way to fetch data with react using socket.io - Stack Overflow

programmeradmin4浏览0评论

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)
    }
  }, [])

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)
    }
  }, [])
Share Improve this question edited Nov 20, 2018 at 4:46 Yangshun Tay 53.1k33 gold badges123 silver badges150 bronze badges asked Nov 19, 2018 at 22:06 ZiiMakcZiiMakc 36.8k25 gold badges74 silver badges121 bronze badges 3
  • 1 This looks like it would work. Is it failing? Also you don't need to pass that empty array to useEffect. It diffs variables passed into that array to decide whether or not to execute the effect callback. – James Commented Nov 19, 2018 at 22:09
  • 1 yes, it's working, i just don't know is it a right way! First time trying sockets. If i will not pass empty array, it's will rerender every sec. With empty array it's like to say "do only onse". – ZiiMakc Commented Nov 19, 2018 at 22:48
  • Oh thats a good point about only wanting this to execute once. Anyways, this looks fine to me. – James Commented Nov 19, 2018 at 22:55
Add a comment  | 

2 Answers 2

Reset to default 14

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 ...;
}

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;
发布评论

评论列表(0)

  1. 暂无评论