Why "State" is empty after action is dispatched? - reactjs

I am working on Log in authentication. After user logged in successfully I want to show log out button based on token value in the state. User gets logged in successfully But actually state is empty and have no values which are passed from express server. So if state token property is empty it does not show log out button after logged in .
api code
import axios from 'axios'
export default {
user:{
login:(credential) => axios.post('/api/auth',{credential}).then(res => res.data.user)
}
}
action code
import {USER_LOGGED_IN} from '../types'
import api from '../api'
export const userLoggedIn = user => ({
type: USER_LOGGED_IN,
user
});
export const login = (credential) =>(dispatch) => api.user.login(credential).then(user => dispatch(userLoggedIn(user)))
type code
export const USER_LOGGED_IN = 'USER_LOGGED_IN'
user reducer
import { USER_LOGGED_IN } from "../types";
export default function user(state ={}, action = {}) {
switch (action.type) {
case USER_LOGGED_IN:
return action.user;
default:
return state;
}
}
rootReducer
import { combineReducers } from "redux";
import user from "./reducers/user";
// import books from "./reducers/books";
export default combineReducers({
user:()=>({
})
});
HomePage
import React from 'react'
import {connect} from 'react-redux'
import PropTypes from 'prop-types'
import { Link } from 'react-router-dom'
function HomePage({isAuthenticated}) {
return (
<div>
<h1>Home Page</h1>
{ isAuthenticated ? <button>Logout</button> : <Link to ="/login">Login</Link>}
</div>
)
}
HomePage.propTypes ={
isAuthenticated:PropTypes.bool.isRequired
}
function mapStateToProps(state){
console.log(state.user.token); // this shows undefined
return{
isAuthenticated:!!state.user.token
}
}
export default connect(mapStateToProps)(HomePage)
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom'
import 'semantic-ui-css/semantic.min.css'
import {createStore,applyMiddleware} from 'redux'
import {Provider} from 'react-redux'
import thunk from 'redux-thunk'
import {composeWithDevTools} from 'redux-devtools-extension'
import App from './App';
import * as serviceWorker from './serviceWorker';
import rootReducer from './rootReducer'
const store = createStore(rootReducer,composeWithDevTools(applyMiddleware(thunk)))
ReactDOM.render(<BrowserRouter><Provider store={store}><App /></Provider></BrowserRouter>, document.getElementById('root'));
serviceWorker.unregister();

update rootreduer file to
import { combineReducers } from "redux";
import user from "./reducers/user";
// import books from "./reducers/books";
export default combineReducers({
user
});

Actually you are not providing your user reducer in combine reducers, you can do as
import { combineReducers } from "redux";
import user from "./reducers/user";
export default combineReducers({
user
})
});
Hope it helps

Related

React- How to make states persists after page refresh?

I made a single page application using React.js with a login page and perfil page. All works well but recently I noticed when refresh my page, all states are empty. Please someone can say me how to fix that issue, I mean what library import and where add it
my index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/App.jsx';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
serviceWorker.unregister();
this is my App.jsx
import React from 'react';
import '../App.css';
import AppRoutes from './AppRoutes';
import { Provider } from "react-redux";
import store from '../redux/store'
store.dispatch(getTallerList())
const App = () => (
<Provider store={store}>
<AppRoutes />
</Provider> )
export default App;
and my store.js
import { applyMiddleware, combineReducers, createStore } from 'redux'
import { ADD_TO_CART, GET_COURSE_LIST, USUARIO_LOGIN } from './action'
import { composeWithDevTools } from 'redux-devtools-extension'
import { persistStore, persistReducer } from 'redux-persist'
import thunk from 'redux-thunk'
const initialCart = {
cart:[]
}
const initialCourses ={
courses:[]
}
const initialUser ={
user:{}
}
const cartReducer = ( state = initialCart,action) => {
if(action.type===ADD_TO_CART)
{
if(state.cart.find(c=>c===action.id))
{
return state
}
return{
...state,
cart: state.cart.concat(action.id),
}
}
return state
}
const coursesReducer = (state=initialCourses, action) =>{
console.log(action)
if(action.type === GET_COURSE_LIST){
return {
...state,
courses: action.courses
}
}
return state
}
const userReducer = (state=initialUser, action)=>{
console.log(action)
if(action.type === USER_LOGIN){
return {
...state,
user: action.user
}
}
return state
}
export default createStore(combineReducers({cartReducer, coursesReducer, userReducer}), composeWithDevTools(applyMiddleware(thunk)))
Try to store value in local storage and at the time of page load get value from local storage. If you have some more values you should use redux for data storage.
It's not an issue, it's the way it works. When you refresh the entire app builds once again from the scratch.
But to persist the store upon refresh you can use these redux middlewares -
redux-persist or
redux-storage
Use this configuration as you are using redux-persist. This is my configuration, just change the main app, reducers, and actions according to your need.
import React from 'react';
import ReactDOM from 'react-dom';
import MyApp from './MyApp';
import {Provider} from 'react-redux';
import {applyMiddleware, createStore} from "redux";
import allReducers from './reducers/index';
import {persistReducer, persistStore} from 'redux-persist';
import {PersistGate} from 'redux-persist/integration/react';
import storage from 'redux-persist/lib/storage';
import thunk from "redux-thunk";
const persistConfig = {
key: 'root',
storage,
};
const persistedReducer = persistReducer(persistConfig, allReducers);
let store = createStore(persistedReducer, applyMiddleware(thunk));
let persistor = persistStore(store);
ReactDOM.render(<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<MyApp/>
</PersistGate>
</Provider>, document.getElementById('root'));
Updated : you can do like this
import {combineReducers} from "redux";
import Users from './load-user';
const allReducers = combineReducers({
users: Users,
})
export default allReducers

React.js Unable to get property 'dispatch' of undefined or null reference

Issue Screen Shot
I am facing 'dispatch' of undefined issue when executing the application.
Whether I need to import any package to work with dispatch in React.js?
ProgramManager.js: This is my component this is where I am using dispatch to call action creator.
import React from 'react';
import { bindActionCreators } from 'redux';
import {connect} from 'react-redux'
import {postsActions,postsSelectors} from '../store/userList/index';
class ProgramManager extends React.Component {
constructor(props, context) {
super(props, context);
}
componentDidMount() {
this.fetchPosts({});
}
fetchPosts(params) {
this.context.store.dispatch(postsActions.fetchPosts(params));
}
render() {
return (
<div className="right-container">
....
</div>
) }
}
function mapStatetoProps(state) {
debugger;
return {
//users: state.Users
params: postsSelectors.getParams(state),
posts: postsSelectors.getPosts(state),
};
}
export default connect(mapStatetoProps)(ProgramManager);
Store.js: This is my Redux Root Store file.
import { applyMiddleware, createStore, combineReducers, compose } from 'redux';
import { createEpicMiddleware } from 'redux-observable';
//import { hashHistory } from 'react-router';
import { routerMiddleware } from 'react-router-redux';
import logger from 'redux-logger';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
import rootEpic from './epics';
// const logger = createLogger({ collapsed: true });
const epicMiddleware = createEpicMiddleware();
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
epicMiddleware.run(rootEpic);
export default createStore(
rootReducer,
composeEnhancers(
applyMiddleware(
epicMiddleware,
logger,
// routerMiddleware(hashHistory),
thunk,
)
)
);
epics.js This is my root epic that combines all sub epic files
import { combineEpics } from 'redux-observable';
import { values } from 'lodash';
import * as postsEpics from './userList/epic';
export default combineEpics(
...values(postsEpics)
);
reducer.js This is my root reducer that combines all sub reducers.
import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';
import posts from './userList/reducer';
export default combineReducers({
posts,
routing: routerReducer,
});
epic.js This the sub epic file that actually gets data from the server.
import { keyBy } from 'lodash';
import axios from 'axios';
import querystring from 'querystring';
import { Observable } from 'rxjs/Observable';
import { push } from 'react-router-redux';
import * as actionTypes from './actionType';
import * as postsActions from './actionCreator';
//import * as RequestModel from './request.model';
export function fetchPosts(action$) {
debugger;
alert('fetchPosts');
//RequestModel.entity = "sample";
return action$.ofType(actionTypes.FETCH_COLLECTION)
.map(action => action.payload)
.switchMap(params => {
return Observable.fromPromise(
axios.get(`http://localhost:8081/posts?${querystring.stringify(params)}`)
// axios.get('http://localhost:4040/admin/getTenantUsers')
).map(res => postsActions.fetchPostsSuccess(res.data, params));
});
}
actionCreation.js this is where the disptacher called to get the state data from post
import { keyBy } from 'lodash';
import * as actionTypes from './actionType';
export function fetchPosts(payload) {
debugger;
return { type: actionTypes.FETCH_COLLECTION, payload };
}
export function fetchPostsSuccess(posts, params) {
debugger;
const byId = keyBy(posts, (post) => post.id);
return {type: actionTypes.FETCH_COLLECTION_SUCCESS, payload: {byId, params}};
}
I have never used a context object to dispatch an action.
I think the best way to have an action ready to be dispatched is by using mapDispatchToProps as the second argument of your connect wrapper, it will bring your action as a prop.
import React from 'react';
import { bindActionCreators } from 'redux';
import {connect} from 'react-redux'
import {postsActions,postsSelectors} from '../store/userList/index';
class ProgramManager extends React.Component {
componentDidMount() {
this.props.fetchPosts();
}
render() {
return (
<div className="right-container">
....
</div>
) }
}
function mapStatetoProps(state) {
debugger;
return {
//users: state.Users
params: postsSelectors.getParams(state),
posts: postsSelectors.getPosts(state),
};
}
function mapDispatchToProps() {
return postsActions
}
export default connect(mapStatetoProps, mapDispatchToProps)(ProgramManager);
mapDispatchToProps is usually a function that simply returns a javascript object with your action creators like:
{
fetchPosts: postActions.fetchPosts,
...
}
That's why you can even go more direct (and no need to declare a mapDispatchToProps function):
export default connect(mapStatetoProps, postActions)(ProgramManager);
React-Redux provides dispatch when you connect your components. So you don't have to use the store in the context to dispatch actions. You can simply do this.props.dispatch(actionCreator()) within your connected components.
Note that it only provides dispatch to your component if you do not pass your own mapDispatchToProps. i.e., when you do
connect(mapStateToProps)(Component)
// or
connect()(Component)
// and not
connect(mapStateToProps, mapDispatchToProps)(Component)
If you provide mapStateToProps, however, you are expected to specify the action creators and wrap them around dispatch, so you don't need the dispatch for manual dispatches anymore.

React Redux Saga - Working with multi-saga

I'm trying to implement multiply saga, but for some reason it's stopped working to me, and i don't know why.
Here is my full code:
// store/sagas/sagas/auth.js
import { delay } from 'redux-saga';
import { put, call } from 'redux-saga/effects';
// When the client enter input on email / password textboxes on auth form.
export function* sagaFunction1(action) {
yield call(actions.SomeAction1, { 'testSeting' );
}
// store/sagas/watchers/auth.js
import { takeEvery, all } from 'redux-saga/effects';
import * as actionTypes from '../../actions/actionTypes';
import * as sagas from '../sagas/auth';
export function* watchAuthSaga() {
yield all([
takeEvery(actionTypes.SAGA_FUNCTION1, sagas.sagaFunction1)
}
// store/sagas/rootSaga.js
import { all, fork } from 'redux-saga/effects';
import * as watchers from './rootWatchers';
const sagasList = [
...watchers
];
export default function* rootSaga() {
yield all(sagasList.map(saga => fork(saga)));
}
// store/sagas/rootWatchers.js
import { watchAuthSaga } from './watchers/auth';
export default [watchAuthSaga];
// index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.less';
import { BrowserRouter } from 'react-router-dom';
import { createStore, applyMiddleware, combineReducers, compose } from 'redux';
import { Provider } from 'react-redux';
import createSagaMiddleware from 'redux-saga';
import * as reducers from './store/reducers/reducers';
import rootSaga from './store/sagas/rootSaga';
import { getEnhancers } from './utils/coreUtils';
import App from './containers/App/App';
import registerServiceWorker from './registerServiceWorker';
// For redux development tools
const composeEnhancers = getEnhancers(compose);
const rootReducer = combineReducers({
auth: reducers.authReducer
});
const sagaMiddleware = createSagaMiddleware();
const store = createStore(rootReducer, composeEnhancers(applyMiddleware(sagaMiddleware)));
sagaMiddleware.run(rootSaga);
const app = (
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
);
ReactDOM.render(app, document.getElementById('root'));
registerServiceWorker();
What am i doing wrong?
// store/sagas/rootSaga.js
import * as watchers from './rootWatchers';
const sagasList = [
...watchers ];
Should be import watchers from './rootWatchers', because you are using export default

React-router-redux push action is not working

I am trying to navigate to Main page once authentication is successful, I am using redux-saga for making API calls. Below is my login generator function:
import * as Type from '../actions/types';
import { takeLatest, put, call } from 'redux-saga/effects';
import firebase from 'firebase';
import { push } from 'react-router-redux';
function* loginUser(action) {
const auth = firebase.auth();
try{
console.log(action.user);
const result = yield call([auth, auth.signInWithEmailAndPassword], action.user.email, action.user.password);
console.log('login sucess');
yield put({type: Type.AUTH_SUCCESSFULL, user: action.user, authMessage:'Login Success'});
console.log('done'); //being logged
yield put(push('/home')); /not being redirected to home. Even the browser url is not changing
console.log('pushed'); //being logged
}
catch(error){
console.log(error.message);
yield put({type: Type.AUTH_FAILED, errorMessage: error.message});
}
}
I just installed react-router-redux and tried doing this, Can someone please tell me what I am doing wrong?
I faced the same issue and following is my solution. Code you have added has not an issue. You need do some extra works to make this work.
Reducer.js
import { combineReducers } from 'redux';
import { connectRouter } from 'connected-react-router';
import { History } from 'history'
const redusers = {
...
}
const mainReducer = (history: History) => combineReducers({
router: connectRouter(history),
...redusers
})
export default mainReducer;
Store.js
import { createStore, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga';
import logger from 'redux-logger';
import { createBrowserHistory } from 'history'
import { routerMiddleware } from 'connected-react-router'
import mainReducer from './mainReducer';
import rootSaga from './rootSaga';
export const history = createBrowserHistory()
const configureStore = (preloadedState?: any) => {
const sagaMiddleware = createSagaMiddleware();
const store = createStore(
mainReducer(history),
preloadedState,
applyMiddleware(routerMiddleware(history), logger, sagaMiddleware),
);
sagaMiddleware.run(rootSaga);
return store;
};
export default configureStore;
index.js
import React, { Suspense } from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import * as serviceWorker from './serviceWorker';
import configureStore, { history } from './store';
const store = configureStore();
const app = (
<Provider store={store}>
<App history={history} />
</Provider>
);
App.js - only added necessary code lines bellow
...
import { History } from 'history'
import { ConnectedRouter } from 'connected-react-router'
...
const {history} = props;
...
return(){
...
<ConnectedRouter history={history}>
{routes}
</ConnectedRouter>
...
}
After setting up above things on your app, it will work as you want.
The other thing is we are no longer using BrowserHistory in the app because custom history has implemented.

UncaughtbTypeError: (0 , _redux2.default) is not a function

I'm trying to use react-redux,but getting this error
UncaughtbTypeError: (0 , _redux2.default) is not a function
Index.js
import React from 'react';
import ReactDom from 'react-dom';
import Provider from 'react-redux';
import { createStore } from 'redux';
import reducer from './src/reducers/Index';
import Root from './src/views/Root';
let store = createStore(reducer)
ReactDom.render(
<Provider store={store}>
<Root />
</Provider>,
document.getElementById('content')
);
Reducer (Index.js)
import combineReducers from 'redux';
import Login from './Login';
const reducer = combineReducers({
Login
})
export default reducer
Reducer (Login.js)
export default function login(state = [], action) {
switch (action.type) {
case 'LOGINLOADING':
return false
case 'LOGINLOADED':
return true
default:
return state
}
}

Resources