0

I want to redirect the client after an action has been fired. I heard of react-redux-router, but not sure how to properly implement it in the actions function.

i followed a little bit of this

https://stackoverflow.com/a/42985875/10865515

However, when i submit the validated form, it doesn't redirect or refreshes.

Actions.js

 import { auth as firebaseAuth } from '../firebaseConfig'
 import { push,  browserHistory } from 'react-router-redux';


 export const signUp = (user) => { return (dispatch) => {
  firebaseAuth.createUserWithEmailAndPassword(user.email, user.password)
    .then(() => {
        dispatch({ type: 'SIGNUP_SUCCESS',
        payload: (action, state, res) => {
            return res.json().then(json => {
              browserHistory.push('/');
              return json;
            });
          },
    });
    }).catch((err) => {
        dispatch({ type: 'SIGNUP_ERROR', err});
    });
  }  
}

Reducers.js

const initialState = {
  emailSignUp: '',
  passwordSignUp: '',
  authError: null

}

export default (state = initialState, action) => {
  switch (action.type) {
    case 'SIGNUP_SUCCESS':      
        return ({
            ...state,
            authError: null
        })

    case 'SIGNUP_ERROR':
        console.log('signup error')
        return ({
            ...state,
            authError: action.err.message
        })
    default:
        return state
 }
} 

Register.js

// ...
handleSubmit(event) {
    event.preventDefault();

    const {formData, errors} = this.state;
    const {email, password} = formData;

    const myError = this.props.authError;
    const creds = {
        email,
        password
    }
    const register = this.props.signUp(creds);
    if (register) {
        console.log(creds);

    }
}
Eric Thomas
  • 21
  • 1
  • 2
  • 6
  • Maybe this post will help https://stackoverflow.com/questions/48514773/use-history-push-in-action-creator-with-react-router-v4/48514877#48514877 – Shubham Khatri Jan 04 '19 at 10:39

3 Answers3

4

You should implement the react-router-dom library which gives you access to the history object for navigation.

In your component that is firing the action creator:

import { withRouter } from "react-router-dom"

Then at the bottom of the code, where you call connect:

export default connect(mapStateToProps, mapDispatchToProps)(withRouter(myComponent))

Now you can set up your action creator to take in history as an argument.

Your actions file:

const signUp = (creds, history) => {
//do some logic then go home
   history.push("/")
}

So in your event handler where you call your action creator, pass in history:

handleSubmit(event) {
    event.preventDefault();

    this.props.signUp(creds, this.props.history);
    if (register) {
        console.log(creds);
    }
}
Cool Guy
  • 13,461
  • 2
  • 13
  • 32
  • where would i place the `history.push` in within the action creator ? if anything im not sure where to place it at. thanks btw. – Eric Thomas Jan 04 '19 at 10:59
  • i did it like this `firebaseAuth.createUserWithEmailAndPassword(user.email, user.password) .then((history) => { dispatch({ type: 'SIGNUP_SUCCESS' }, history.push('/') ); }).catch((err) => { dispatch({ type: 'SIGNUP_ERROR', err}); }); }` – Eric Thomas Jan 04 '19 at 11:07
  • im getting `history.push is not a function` – Eric Thomas Jan 04 '19 at 11:09
  • You would need a overall function that executes the firebase action. Something like const signIn = (history) => {your firebase code}.then((res) history.push("/") . Then make sure you call that function in your component – Cool Guy Jan 04 '19 at 11:16
  • im following this https://stackoverflow.com/questions/48514773/use-history-push-in-action-creator-with-react-router-v4/48514877#48514877 – Eric Thomas Jan 04 '19 at 11:18
  • ok this is what i have so far `firebaseAuth.createUserWithEmailAndPassword(user.email, user.password) .then(() => { dispatch({ type: 'SIGNUP_SUCCESS' }). history.push('/'); }).catch((err) => { dispatch({ type: 'SIGNUP_ERROR', err}); }); }` – Eric Thomas Jan 04 '19 at 11:25
  • This answer solves the problem to history.push() from a redux action. Just adjust your action to receive a history parameter and pass it from the dispatcher. – CodeChuck Oct 13 '20 at 02:07
0

This is my example with firebase and redux:

Inside your actions you create the user and dispatch appropriate actions to your reducer:

export const signUp = newUser => {
  return (dispatch, getState, { getFirebase, getFirestore }) => {
    const firebase = getFirebase();
    const firestore = getFirestore();

    firebase
      .auth()
      .createUserWithEmailAndPassword(newUser.email, newUser.password)
      .then(resp => {
        return firestore
          .collection('users')
          .doc(resp.user.uid)
          .set({
            firstName: newUser.firstName,
            lastName: newUser.lastName
          });
      })
      .then(() => {
        dispatch({ type: 'SIGNUP_SUCCESS' });
      })
      .catch(err => {
        dispatch({ type: 'SIGNUP_ERROR', err });
      });
  };
};

In your reducer you handle the state change:

    case 'SIGNUP_SUCCESS':
        return {
            ...state,
            authError: null
        };
    case 'SIGNUP_ERROR':
        return {
            ...state,
            authError: action.err.message
        };

In the component you import Redirect from react-router-dom and redirect on successful signup:

    import { Redirect } from 'react-router-dom';

    class SignIn extends Component {
        state = {
            email: '',
            password: ''
        };
        handleChange = e => {
            this.setState({
                [e.target.id]: e.target.value
            });
        };
        handleSubmit = e => {
            e.preventDefault();
            this.props.signIn(this.state);
        };
        render() {
            const { authError, auth } = this.props;
            if (auth.uid) return <Redirect to="/" />;
            return (
                <div className="container">
                    <form onSubmit={this.handleSubmit} className="white">
                     ...
                    </form>
                </div>
            );
        }
    }

    const mapStateToProps = state => {
        return {
            authError: state.auth.authError,
            auth: state.firebase.auth
        };
    };

    const mapDispatchToProps = dispatch => {
        return {
            signIn: creds => dispatch(signIn(creds))
        };
    };

    export default connect(
        mapStateToProps,
        mapDispatchToProps
    )(SignIn);
Vrle
  • 672
  • 8
  • 17
  • thank you for the code, it help me understand redux very much so by the examples. However, i don't want to do it this way. I just want to simply redirect after a user signs up. I want something that i can add with the existing code i have. I wont really learn anything if i just copied that. – Eric Thomas Jan 04 '19 at 11:04
  • Well I didn't really put it here for the copying purposes. This is (in my opinion) a proper way you should look at redirects with `redux`. At first I wanted to just give you a short snippet of the code but putting all of it here I think everyone can understand what's actually going on. – Vrle Jan 04 '19 at 11:10
  • everything looks great, except for the firestore. I'm not using firestore. So that is why im somewhat hesitate using this code. – Eric Thomas Jan 04 '19 at 11:11
  • thx, but im referencing this atm https://stackoverflow.com/questions/48514773/use-history-push-in-action-creator-with-react-router-v4/48514877#48514877 – Eric Thomas Jan 04 '19 at 11:18
  • No probs - solving stuff in different ways will make you a better problem solver :) Don't use the code, but the data flow patterns only. Component create user > action > get action response > reducer changes state > components gets what happened from the redux state and acts accordingly. If your signup succeeded it redirects, if not you get a message displayed. – Vrle Jan 04 '19 at 11:21
0

I fixed it.

Apart of the problem stemmed from this

React history.push() is updating url but not navigating to it in browser

and not knowing how to properly use then

I needed to wrap the router tags around the app

import App from './App';
import * as serviceWorker from './serviceWorker';
import { createStore, applyMiddleware, compose } from 'redux'
import { Provider } from 'react-redux';
import rootReducer from './reducers';
import thunk from 'redux-thunk'
import { BrowserRouter as Router } from 'react-router-dom';
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);

ReactDOM.render(
    <Provider store={createStoreWithMiddleware(rootReducer)}>
     <Router >
      <App />
      </Router>
    </Provider>

    ,document.getElementById('root'));

and update my actions like so

import { push } from 'react-router-redux';
import { history } from '../components/Navbar';

export const signUp = (user) => { return (dispatch) => {
    firebaseAuth.createUserWithEmailAndPassword(user.email, user.password)
        .then(() => {
            dispatch({ type: 'SIGNUP_SUCCESS' })
        }).then((response) => {
            history.push('/');
        }).catch((err) => {
            dispatch({ type: 'SIGNUP_ERROR', err});
        });


    }

}

Navbar.js

import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
import createBrowserHistory from 'history/createBrowserHistory';
import SignUp from './SignUp';
import SignIn from './SignIn';
// forcerefresh does the magic.
export const history = createBrowserHistory({forceRefresh:true});

const Navbar = () => {
    return(

    <Router history={history}>
        <div>
        <nav className="navbar navbar-expand-lg navbar-light bg-light ">
          <button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExample08" aria-controls="navbarsExample08" aria-expanded="false" aria-label="Toggle navigation">
            <span className="navbar-toggler-icon"></span>
          </button>

          <div className="collapse navbar-collapse justify-content-md-center" id="navbarsExample08">
            <ul className="navbar-nav">

                <li className="nav-item">
                    <Link className="nav-link" to="/">Home </Link>
                </li>
                <li className="nav-item">
                    <Link className="nav-link" to="/SignUp">Sign Up </Link>
                </li>
                <li className="nav-item">
                    <Link  className="nav-link" to="/SignIn">Sign In </Link>
                </li>


            </ul>
          </div>
        </nav>
        <Route path="/SignUp" component={SignUp} />
        <Route path="/SignIn" component={SignIn} />
        <Route path="/"/>


    </div>
    </Router>
    );

}

export default Navbar;

Keep my existing handleSubmit function the same.

Eric Thomas
  • 21
  • 1
  • 2
  • 6