如何在反应中处理全球用户/用户数据?

Posted

技术标签:

【中文标题】如何在反应中处理全球用户/用户数据?【英文标题】:How to treat global user/user data in react? 【发布时间】:2020-01-22 03:39:39 【问题描述】:

我正在构建一个 ReactJS 项目,我正在使用类似的东西通过应用程序提供用户数据:

function Comp1() 
  const [user, setUser] = useState();

  firebase.auth().onAuthStateChanged(function (_user) 
    if (_user) 
      // User is signed in.
      // do some firestroe queryes to get all the user's data
      setUser(_user);
     else 
      setUser( exists: false );
    
  );

  return (
    <div>
      <UserProvider.Provider value=user, setUser>
        <Comp2 />
        <Comp3 />
      </UserProvider.Provider>

    </div>
  );


function Comp2(props) 
  const  user, setUser  = useContext(UserProvider);
  return (
    <div>
      user.exists
    </div>
  )


function Comp3(props) 
  const  user, setUser  = useContext(UserProvider);
  return (
    <div>
      user.exists
    </div>
  )


//User Provider

import React from 'react';

const UserProvider = React.createContext();
export default UserProvider;

因此,在这种情况下,Comp1 将用户数据提供给 Comp2 和 Comp3。唯一的问题是当用户状态改变或页面加载时,它会创建一个无限循环。如果我没有使用 useState 来存储用户数据,那么当它发生变化时,组件不会被重新渲染。我也尝试在 index.js 文件中做这样的事情:

firebase.auth().onAuthStateChanged(function (user) 
  if (user) 
     ReactDOM.render(<Comp1 user=user />, document.getElementById('root'));
   else  
     ReactDOM.render(<Comp1 user=exists: false />, document.getElementById('root'));
  
);

但这有时会有点奇怪,而且有点乱。有哪些解决方案?谢谢。

编辑:我试图以错误的方式做这件事?我应该如何仅通过一个 firebase 查询来提供所有用户数据?

【问题讨论】:

您将用户初始化为,因此很有可能在第一次渲染时没有设置user.exists 这应该不是问题。因为我的问题是,它将是一个不断变化的变量,我想避免无限循环。 你能创建一个 JSFiddle 以便我们重现它吗? 你可能需要尝试使用redux 【参考方案1】:

问题:-

发生循环的问题是由于 Comp1 的编写方式。 在 Comp1 功能组件中编写的任何语句都将在 prop 或 state 发生更改后执行。因此,在这种情况下,每当setUser 被调用时,Comp1 都会重新渲染并再次订阅身份验证更改侦听器,该侦听器在接收数据时再次执行 setUser。

function Comp1() 
  const [user, setUser] = useState();

  firebase.auth().onAuthStateChanged(function (_user) 
    if (_user) 
      // User is signed in.
      // do some firestroe queryes to get all the user's data
      setUser(_user);
     else 
      setUser( exists: false );
    
  );

  return (
    <div>
      <UserProvider.Provider value=user, setUser>
        <Comp2 />
        <Comp3 />
      </UserProvider.Provider>

    </div>
  );


解决办法:-

您可以使用 useEffect 使语句在 componentDidMountcomponentDidUdatecomponentWillUnmount react 的生命周期上执行。

   // [] is passed as 2 args so that this effect will run once only on mount and unmount.

  useEffect(()=> 
    const unsubscribe = firebase.auth().onAuthStateChanged(function (_user) 
      if (_user) 
        // User is signed in.
        // do some firestroe queryes to get all the user's data
        setUser(_user);
       else 
        setUser( exists: false );
      
    );

    // this method gets executed on component unmount.
    return () => 
      unsubscribe();
    
  , []);

我为上述情况创建了一个副本,您可以检查它运行here

【讨论】:

【参考方案2】:

免责声明:我没有使用 ReactJS 的经验,所以我可能完全错了,如果它没有帮助,请批评或否决这个答案。正如匈牙利谚语所说,这实际上是在尝试将干净的水倒入玻璃杯中。

看看这个逻辑:

function Comp1() 
  const [user, setUser] = useState();

  firebase.auth().onAuthStateChanged(function (_user) 
    if (_user) 
      // User is signed in.
      // do some firestroe queryes to get all the user's data
      setUser(_user);
     else 
      setUser( exists: false );
    
  );

  return (
    <div>
      <UserProvider.Provider value=user, setUser>
        <Comp2 />
        <Comp3 />
      </UserProvider.Provider>

    </div>
  );

无论如何,您都在致电setUser。相反,您应该检查user 是否已经设置,如果是,它是否与_user 匹配。当且仅当_user 不同于user 时,将user 设置为_user。顺其自然,setUser 被触发,触发Comp2Comp3 变化,触发上面的事件触发setUser

【讨论】:

【参考方案3】:

Comp1 中,一个新的onAuthStateChanged 观察者会在每次渲染 时添加到firebase.Auth

将该声明放在useEffect 中,例如:

useEffect(() => 
    firebase.auth().onAuthStateChanged(function (_user) 
    if (_user) 
      // User is signed in.
      // do some firestroe queryes to get all the user's data
      setUser(_user);
     else 
      setUser( exists: false );
    
  );
, []);

【讨论】:

【参考方案4】:

我建议为应用程序使用一些状态容器,以便用户轻松操作。最常见的解决方案是使用Redux。使用 redux,您将能够拥有应用程序的全局状态。通常,所有用户数据都存储在其中。 https://redux.js.org/ 另一种解决方案是使用MobX 进行简单的商店访问。如果您不喜欢它,它不会使用 Flux 模式。 如果您不想使用全局存储,您可以使用HOCs 来传播您的用户数据。最后,您可以使用Context 进行 React,但这是不好的方法。

例如,让我们选择最流行的 Flux 架构代表 - Redux。

第一层是视图层。在这里,我们将调度一些操作来更改全局,例如用户数据。

import  connect  from 'react-redux'
import  bindActionCreators  from 'redux' 
import  logIn, logOut  from 'actions'

export default class Page extends React.Component 
  useEffect(() => 
    firebase.auth().onAuthStateChanged((user) => 
      logIn(user)
     else 
      logOut()
   )
  , [])

  render () 
    ...
  


const mapDispatchToProps = dispatch => bindActionCreators(
  logIn,
  logOut
, dispatch)

export default connect(null, mapDispatchToProps)(App)

第二层是动作。在这里,我们使用我们的数据,使用 api,格式状态等。 action 的主要目标是创建数据以将其传递给 reducer。

actions.js

export const logIn = user => dispatch => 
  // dispatch action to change global state    
  dispatch(
    type: 'LOG_IN',
    payload: user
  )


export const logOut = user => dispatch => 
  dispatch( type: 'LOG_OUT' )

最后一件事是减速器。它们的唯一目标是改变状态。我们在这里订阅一些操作。 Reducer 应该是一个纯函数。状态不应该被改变,只能被覆盖。

appReducer.js

const initialState = 
 user: null


export default function appReducer (state = initialState, action) 
   const  type, payload  = action
   switch(type) 
     case 'LOG_IN':
       return 
          ...state,
          user: payload
       
     case: 'LOG_OUT':
       return 
          ...state,
          user: null
       
   

然后,我们可以随时使用全局应用状态。 为此,我们应该使用react-redux 库和Provider HOC

const App = () =>
  <Provider store=store>
     <Navigation /> 
  </Provider>

现在,我们可以通过 react-redux connect HOF 访问任何组件内的任何商店。它可以在其中使用 React Context API。

const Page2 = ( user ) => 
   //... manipulate with user


// this function gets all stores that you have in the Provider.
const mapStateToProps = (state) => 
  user: state.user



export default connect(mapStateToProps)(Page2)

顺便说一句,您应该选择中间件来处理 redux 中的异步代码。在我的示例中使用最流行的是redux-thunk。 您可以在官方文档中找到更多信息。 在那里您可以找到有关如何进行初始存储配置的信息

【讨论】:

哇...我没有这么高的理解水平。能详细解释一下吗? 如有任何问题,请咨询。我建议你创建一个简单的 todo 项目来理解主要概念 你这是什么意思? 我的意思是尝试创建一个简单的应用程序,您将在其中拥有以下功能 1. 将项目添加到 TODO 列表 2. 从 TODO 列表中删除项目 3. 更改 TODO 列表中的项目 使用 redux 如果你不想使用 Redux,我会在 React 中使用 useReducer 钩子。

以上是关于如何在反应中处理全球用户/用户数据?的主要内容,如果未能解决你的问题,请参考以下文章

如何将默认反应上下文值设置为来自 Firestore 的数据?

2014Esri全球用户大会之影像和栅格

如果用户禁用了他们的密码,如何对 iOS 数据保护做出反应?

在 Meteor 中按与当前用户的距离对用户进行反应性排序?

机器学习+全球数据库,一攻一守读取全球化未来丨Q推荐

在反应中处理多个帐户