【问题标题】:ContextAPI: How would one share data between contexts in React?ContextAPI:如何在 React 的上下文之间共享数据?
【发布时间】:2020-03-05 18:52:04
【问题描述】:

这是我的问题:

我的网站的身份验证位是“完整的”。注册/登录后,用户会收到一个包含 jwt 令牌的 cookie。收到后,UserDataContext 会被赋予用户的用户 ID (uid) 和用户名。 uid 用于服务器上的各种事物。

Socket.io 将用于告诉后端从前端做一些事情。 Socket 可用于通过反应上下文反应组件。我有 2 个上下文,UserDataContext 和 SocketContext。我需要跟踪哪个套接字连接到哪个用户。

我想我会在服务器上创建一个 SocketManager 类,它监听新的套接字连接并通过 uid 将它们与用户相关联。我尝试使用 Socket 的 id 执行此操作,但这样做的问题是 id 在重定向/刷新时发生更改。所以,我需要一种方法来发送带有套接字连接事件的 uid。 uid 保存在 UserDataContext 中。

如何将 uid 从 UserDataContext 获取到 SocketContext?我应该合并这些上下文吗?这甚至是正确的方向吗?

App.js -

import React, { Component } from 'react';
import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom';

import UserDataContextProvider from './contexts/UserDataContext.js';
import SocketContextProvider from './contexts/SocketContext.js';

import Landing from './pages/Landing.js';
import Topnav from './pages/components/global/Topnav.js';
import UserDashboard from './pages/components/consoles/UserDashboard.js';

import './Global.css';


const App = props => (
  <div>
    <UserDataContextProvider>
      <Topnav />
      <SocketContextProvider>
        <Switch>
          <Route exact path='/' component={Landing} />
          <Route exact path='/dashboard/:id' component={UserDashboard} />
        </Switch>
      </SocketContextProvider>
    </UserDataContextProvider>
  </div>
);

export default App;

UserDataContext.js -

import React, { Component, createContext } from 'react';

export const UserDataContext = createContext();

class UserDataContextProvider extends Component{
  state = {
    auth: false,
    uid: false,
    username: false
  };

  setUserData = data => {
    this.setState({uid: data.uid, username: data.tag})
  }

  render(){
    return (
      <UserDataContext.Provider value={{state: this.state, setUserData: this.setUserData}}>
        {this.props.children}
      </UserDataContext.Provider>
    );
  }
}

export default UserDataContextProvider;

SocketContext.js -

import React, { Component, createContext } from 'react';

export const SocketContext = createContext();

import io from 'socket.io-client';
const prod = (process.env.NODE_ENV === "production");
const address = prod ? window.location.hostname : "http://localhost:8080";
const socket = io(address);

class SocketContextProvider extends Component{
  render(){
    return (
      <SocketContext.Provider value={{socket:socket}}>
        {this.props.children}
      </SocketContext.Provider>
    );
  }
}

export default SocketContextProvider;

【问题讨论】:

    标签: javascript reactjs socket.io react-context


    【解决方案1】:

    您需要将事物嵌套到 SocketContextProvider 使用用户数据上下文(或合并两个提供程序)。然后你可以:

    • 发送一条特殊消息,在新的 uid 更改时通知服务器,或者
    • 在您发送的每条消息中发送 uid

    也就是说,使用上下文和提供程序主要用于传播状态更改(例如用户名、主题、诸如此类),而不是像那个套接字这样的“活动对象”。

    对于您的情况,由于您已经有一个用于套接字的模块级全局,因此放弃套接字的提供者和上下文并不是什么大罪,而只是

    import io from 'socket.io-client';
    const prod = (process.env.NODE_ENV === "production");
    const address = prod ? window.location.hostname : "http://localhost:8080";
    
    let socket = null;
    
    export function getSocket() {
      if(socket === null) {
        socket = io(address);
      }
      return socket;
    }
    

    import { getSocket } from './socket-things';
    
    const MyComponent = () => (
      <button onClick={() => getSocket().send('foo')} />
    );
    

    【讨论】:

    • 我想我在教程中看到它与上下文一起使用,然后就使用了。这是一个更好的主意。当您说这不是“大罪”时,您是在暗示这是一个较小的罪并且有更好的方法来做到这一点?
    • 拥有全局状态使得孤立地测试事物变得更加困难。也就是说,这也是相当可测试的,因为您可以添加机制以将 socket 对象替换为合适的对象。
    • Ps:我编辑了一些东西,只在第一次真正需要的时候创建和连接套接字。
    • 如果你使用服务器端渲染,人们将共享套接字...
    • @quirimmo 好吧,不,Web 浏览器中的客户端无法与服务器上的同一个套接字对象进行交互。在 OP 的用例中,似乎所有套接字交互都经过了身份验证。
    猜你喜欢
    • 2018-11-20
    • 2019-08-20
    • 2013-07-10
    • 2012-03-15
    • 2020-11-08
    • 2022-01-01
    • 1970-01-01
    • 2019-05-16
    相关资源
    最近更新 更多