如何反应全局用户/用户数据?

Bud*_*Örs 2 javascript firebase reactjs firebase-authentication react-hooks

我正在构建一个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;
Run Code Online (Sandbox Code Playgroud)

因此,在这种情况下,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'));
  }
});
Run Code Online (Sandbox Code Playgroud)

但这有时有些奇怪,而且有点混乱。有什么解决方案?谢谢。

编辑:我正在努力以错误的方式做到这一点?如何仅通过一个Firebase查询提供所有用户数据?

Pra*_*aud 5

I suggest using some state container for the application to easily manipulate with a user. The most common solution is to use Redux. With redux, you will be able to have a global state of your app. Generally, all user data stored in it. https://redux.js.org/ The other solution is to use MobX with simple store access. It doesn't use Flux pattern if you don't like it. If you don't want to use a global store you can use HOCs to propagate your user data. Finally, you can use Context to React, but it is bad approach.

Let's, for example, choose the most popular representer of Flux architecture - Redux.

在此处输入图片说明

The first layer is the View layer. Here we will dispatch some action to change global, e.g user data.

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)
Run Code Online (Sandbox Code Playgroud)

The second layer are actions. Here we work we with our data, working with api, format state and so on. The main goal of actions is to create data to pass it to the 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' })
}
Run Code Online (Sandbox Code Playgroud)

The last thing is the reducers. The only goal of them is to change state. We subscribe here for some actions. Reducer always should be a pure function. State shouldn't be mutated, only overwritten.

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
       }
   }
}
Run Code Online (Sandbox Code Playgroud)

Then, we can work with the global app state whenever we want. To do it, we should use react-redux library and Provider HOC

const App = () =>
  <Provider store={store}>
     <Navigation /> 
  </Provider>
Run Code Online (Sandbox Code Playgroud)

Now, we can have access to any stores inside any component though react-redux connect HOF. It works with React Context API inside of it.

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)
Run Code Online (Sandbox Code Playgroud)

By the way, you should choose middleware to work with async code in redux. The most popular that is used in my example is redux-thunk. More information you can find in the official documentation. There you can find information about how to make initial store configuration