如何在反应中处理全球用户/用户数据?
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 使语句在 componentDidMount
、componentDidUdate
和 componentWillUnmount
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
被触发,触发Comp2
和Comp3
变化,触发上面的事件触发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 的数据?
如果用户禁用了他们的密码,如何对 iOS 数据保护做出反应?