У меня проблемы с useSelector
хуком в приложении Гэтсби. js. Ловушка не может достичь значения store
в Redux, если store
явно не экспортирован из файла установки Redx и импортирован в файл компонента. Похоже, что <Provider>
недоступно (?).
Ошибка: ошибка при выборе состояния хранилища: невозможно прочитать свойство 'user' из неопределенного
Я думаю, что я несколько испортил настройку redux или gatsby-browser // ssr, но я не знаю, как именно ... Любая помощь будет признательна.
// App.js
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { store } from '../store/ReduxWrapper' // <-- store needs to be imported to be accessible ?!
import { loadFirebaseInitialize } from '../store/app'
import AuthenticatedApp from '../containers/AuthenticatedApp'
import UnauthenticatedApp from '../containers/UnauthenticatedApp'
const App = () => {
const dispatch = useDispatch()
const firebase = useSelector(store => store.app.firebase) // <-- store is undefined unless explicitely imported
const user = useSelector(store => store.auth.user) // <-- store is undefined unless explicitely imported
if (!firebase) {
dispatch(loadFirebaseInitialize())
return (
<div>
<p>Loading firebase</p>
</div>
)
} else {
return user ? <AuthenticatedApp /> : <UnauthenticatedApp />
}
}
export default App
// ReduxWrapper.js
import React from 'react'
import { Provider } from 'react-redux'
import { applyMiddleware, createStore } from 'redux'
import thunk from 'redux-thunk'
import rootReducer from '.';
export const store = createStore(
rootReducer,
applyMiddleware(thunk)
)
export default ({ element }) => (
<Provider store={store}>{element}</Provider>
);
// gatsby-browser.js / gatsby-ssr.js
export { default as wrapRootElement } from './src/store/ReduxWrapper'
РЕДАКТИРОВАТЬ : добавление дополнительного кода, относящегося к избыточному:
// index.js
import { combineReducers } from 'redux';
import app from './app';
import auth from './auth';
export default combineReducers({ app, auth });
// auth.js
// to get state from a sibling ('app.js') store
import { store } from '../store/ReduxWrapper'
const initialState = {
isAuthenticated: false,
isAuthPending: false,
error: null,
user: null,
}
const AUTH_REQUEST_INITIALIZE = 'AUTH_REQUEST_INITIALIZE'
const AUTH_REQUEST_SUCCESS = 'AUTH_REQUEST_SUCCESS'
const AUTH_REQUEST_ERROR = 'AUTH_REQUEST_SUCCESS'
const AUTH_LOGOUT = 'AUTH_LOGOUT'
export const authRequestInitialize = () => ({
type: AUTH_REQUEST_INITIALIZE,
})
export const authRequestSuccess = user => ({
type: AUTH_REQUEST_SUCCESS,
user,
})
export const authRequestError = error => ({
type: AUTH_REQUEST_ERROR,
error,
})
export const authLogin = ({ email, password }) => dispatch => {
const { firebase } = store.getState().app
if (firebase) {
dispatch(authRequestInitialize())
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then(user => console.log(`user`, user) || dispatch(authRequestSuccess(user)))
.catch(error => console.log(`authError`, error) || dispatch(authRequestError(error)))
}
}
export const authLogout = () => dispatch => {
const { firebase } = store.getState().app
if (firebase) {
firebase.auth().signOut().then(
res => dispatch({ type: AUTH_LOGOUT })
)
}
}
export default (state = initialState, action) => {
switch (action.type) {
case AUTH_REQUEST_INITIALIZE:
return { ...state, isAuthPending: true }
case AUTH_REQUEST_SUCCESS:
return { ...state, user: action.user }
case AUTH_REQUEST_ERROR:
return { ...state, error: action.error }
case AUTH_LOGOUT:
return { ...state, user: null }
default:
return state
}
}
//app.js
import getFirebase from '../firebase'
const initialState = {
firebase: null,
isFirebaseLoading: false,
}
const LOAD_FIREBASE_INITIALIZE = 'LOAD_FIREBASE_INITIALIZE'
const LOAD_FIREBASE_SUCCESS = 'LOAD_FIREBASE_SUCCESS'
const LOAD_FIREBASE_ERROR = 'LOAD_FIREBASE_ERROR'
export const loadFirebaseInitialize = () => {
return dispatch => {
const app = import('firebase/app')
const auth = import('firebase/auth')
const firestore = import('firebase/firestore')
Promise.all([app, auth, firestore])
.then(values => {
const firebase = getFirebase(values[0])
dispatch(loadFirebaseSuccess(firebase))
})
.catch(error => dispatch(loadFirebaseError(error)))
}
}
export const loadFirebaseSuccess = firebaseObj => ({
type: LOAD_FIREBASE_SUCCESS,
firebaseObj,
})
export const loadFirebaseError = error => ({
type: LOAD_FIREBASE_ERROR,
error,
})
export default (state = initialState, action) => {
switch (action.type) {
case LOAD_FIREBASE_INITIALIZE:
return { ...state, isFirebaseLoading: true }
case LOAD_FIREBASE_SUCCESS:
return {
...state,
isFirebaseLoading: false,
firebase: action.firebaseObj,
}
case LOAD_FIREBASE_ERROR:
return { ...state, isFirebaseLoading: false, error: action.error }
default:
return state
}
}