我正在使用Firebase身份验证,redux和Next.js构建应用。
我选择在我的应用中实现redux,因为我想使用可以与每个组件共享的状态(即已登录的用户)。
不知道为什么我无法从store
获取状态,并显示未定义来自mapStateToProps
的道具的情况。
这是我的代码:
/pages/_app.js
import '../styles/global.css'
import 'bootstrap/dist/css/bootstrap.min.css'
import React from 'react'
import App from 'next/app'
import Layout from '../components/UI/Layout'
import { createStore, compose } from 'redux'
import { Provider } from 'react-redux'
import withRedux from 'next-redux-wrapper'
import rootReducer, { initialState } from '../store/reducer'
const composeEnhancers = (typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose;
const makeStore = (initialState) => {
return createStore(rootReducer, initialState)
}
class MyApp extends App {
static async getInitialProps({Component, ctx}) {
const pageProps = Component.getInitialProps ? await Component.getInitialProps(ctx) : {};
//Anything returned here can be accessed by the client
return {pageProps: pageProps};
}
render() {
//pageProps that were returned from 'getInitialProps' are stored in the props i.e. pageprops
const {Component, pageProps, store} = this.props;
return (
<Provider store={store}>
<Layout>
<Component {...pageProps}/>
</Layout>
</Provider>
);
}
}
export default withRedux(makeStore)(MyApp)
/store/actions.js
export const SIGNIN = 'SIGNIN'
/store/reducer.js
import * as actions from '../store/actions'
import { combineReducers } from 'redux'
export const initialState = {
isSignedIn: false, // Signed-in state.
currentUser: null // Current user signed-in.
}
const signInReducer = (state = initialState, action) => {
switch (action.type) {
case actions.SIGNIN:
return {
...state,
isSignedIn: !!action.payload,
currentUser: action.payload
}
default:
return {...state};
}
}
const rootReducer = combineReducers({ signIn: signInReducer })
export default rootReducer
/pages/index.js
import Head from 'next/head'
import Link from 'next/link'
import { connect } from 'react-redux'
const Home = (props) => {
console.log("props " + props)
return (
<div className="container">
<Head>
<title>Create Next App</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main>
<h1 className="title">
Welcome to <a href="https://github.com/rickywychoi/BBY-32-Nutrition-Plus" target="_blank">Nutrition+!</a>
</h1>
<br/>
<h2>
<b>This is [dev] branch.</b>
</h2>
<br/>
{!props.isSignedIn
?
(<Link href="/login"><a id="signIn">Sign in</a></Link>)
:
(<p>Hello {props.currentUser.displayname}!</p>)
}
</main>
<footer>
<a
href="https://zeit.co?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Powered by <img src="/zeit.svg" alt="ZEIT Logo" />
</a>
</footer>
</div>
)
}
const mapStateToProps = state => {
return {
isSignedIn: state.isSignedIn,
currentUser: state.currentUser
}
}
export default connect(mapStateToProps)(Home)
/pages/login.js
import Link from 'next/link'
import loginStyles from '../styles/login.module.css'
// Import FirebaseAuth and firebase.
import React from 'react';
import StyledFirebaseAuth from 'react-firebaseui/StyledFirebaseAuth';
import firebase from 'firebase';
import firebaseConfig from '../firebaseConfig'
import { connect } from 'react-redux'
import * as actions from '../store/actions'
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
}
class SignInScreen extends React.Component {
constructor(props) {
super(props);
}
// Configure FirebaseUI.
uiConfig = {
callbacks: {
signInSuccessWithAuthResult: function (authResult, redirectUrl) {
// User successfully signed in.
// Return type determines whether we continue the redirect automatically
// or whether we leave that to developer to handle.
return true;
}
},
// Popup signin flow rather than redirect flow.
signInFlow: 'popup',
// signInSuccessUrl: '/',
// We will display Google and Facebook as auth providers.
signInOptions: [
firebase.auth.EmailAuthProvider.PROVIDER_ID,
firebase.auth.GoogleAuthProvider.PROVIDER_ID
],
// Terms of service url.
tosUrl: '/',
// Privacy policy url.
privacyPolicyUrl: '/'
};
// Listen to the Firebase Auth state and set the local state.
componentDidMount() {
this.unregisterAuthObserver = firebase.auth().onAuthStateChanged(
// (user) => this.setState({
// isSignedIn: !!user,
// currentUser: user
// })
(user) => this.props.onSignIn(user)
);
}
// Make sure we un-register Firebase observers when the component unmounts.
componentWillUnmount() {
this.unregisterAuthObserver();
}
render() {
if (!this.props.isSignedIn) {
return (
<div className={loginStyles.main}>
<h1>Nutrition+</h1>
<p>Please sign-in:</p>
<br/>
<StyledFirebaseAuth uiConfig={this.uiConfig} firebaseAuth={firebase.auth()}/>
<br/>
<Link href="/"><a>Back to home</a></Link>
</div>
);
}
return (
<div>
<h1>Nutrition+</h1>
<p>Welcome {this.props.currentUser.displayname}! You are now signed-in!</p>
<a onClick={() => firebase.auth().signOut()}>Sign-out</a>
</div>
);
}
}
const mapStateToProps = state => {
return {
isSignedIn: state.isSignedIn,
currentUser: state.currentUser
}
}
const mapDispatchToProps = dispatch => {
return {
onSignIn: (user) => dispatch({type: actions.SIGNIN, payload: user})
}
}
export default connect(mapStateToProps, mapDispatchToProps)(SignInScreen)
这是控制台工具的屏幕快照,显示initial state
未定义。但是,我在/store/reducer.js中分配了initialState。
我希望看到的是isSignedIn
必须是false
而不是undefined
,而currentUser
必须是null
而不是{{1 }}。
console screenshot
谢谢。