Is there a way to dispatch an action on Component Mount? - reactjs

I'm using Firebase for user authentication and I want to use the onAuthStateChanged() to make a user persist even after refreshing the browser. I'm also using redux-sagas to handle async operations.
Index.jsx file:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import './index.css';
import './App.scss';
import store from './store';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root'));
serviceWorker.unregister();
App.jsx:
import React, { Component } from 'react';
import { Route, BrowserRouter as Router, Switch } from 'react-router-dom';
import { connect } from 'react-redux';
import Navbar from './components/navbar';
import routes from './routes';
import { actionTypes } from './components/signin/actionTypes';
const { VERIFY_REQUEST } = actionTypes;
const mapDispatchToProps = {
VERIFY_REQUEST,
};
class App extends Component {
render() {
return (
<Router>
<Navbar />
<Switch>
{routes.map(route => (
<Route
key={route.path}
path={route.path}
exact={route.exact}
component={route.component}
/>
))}
</Switch>
</Router>
);
}
}
export default connect(null, mapDispatchToProps)(App);
My sagas generator function binded to the action type:
function onAuthState() {
return new Promise((resolve, reject) => {
loginToFirebase.auth().onAuthStateChanged(user => {
if (user) {
console.log(user);
resolve(user);
} else {
reject(new Error('Ops!'));
}
});
});
}
function* verifyUserAuth() {
try {
const LOGIN_API_URL = process.env.REACT_APP_USER_AUTH_API;
const { user } = yield onAuthState();
console.log(user);
const userInfo = { userAuth: user, userType: 'user' };
const config = { headers: { 'Content-Type': 'application/json' } };
const body = JSON.stringify(userInfo);
const response = yield axios.post(LOGIN_API_URL, body, config);
if (response.status === 200) {
const { data: { info } } = response.data;
yield put({ payload: info, type: VERIFY_SUCCESS });
} else yield put(loginError(response.status));
} catch (error) {
yield put(loginError(error));
}
}
export default function* watchUserLoginAction() {
yield takeEvery(VERIFY_REQUEST, verifyUserAuth);
}
Everytime I check my redux tools, I don't see the action being fired on component mount.

You can use the componentDidMount lifecycle method whenever you want to do something after the component is mounted. Modify your mapDispatchToProps
const mapDispatchToProps = dispatch => {
return {
verifyRequest: () => { dispatch( {type : VERIFY_REQUEST} ) }
};
};
and then call verifyRequest from componentDidMount
componentDidMount = () =>{
this.props.verifyRequest()
}
Also, it is better to create action creators instead of directly dispatching the action, like so
export const verifyRequestAction = () => {
return {
type: VERIFY_REQUEST
}
}
and then
const mapDispatchToProps = dispatch => {
return {
verifyRequest: () => { dispatch(verifyRequestAction()}
};
};

Related

React useContext is returning undefined

I am trying to use context with my Gatsby project. I have successfully implemented this in my previous project and I have copied the code over to my new project and it's not working as intended.
This is my context.js file:
import React, { useContext, useState } from "react";
const defaultState = {
isLoggedIn: false,
};
const AuthContext = React.createContext();
export function useAuth() {
return useContext(AuthContext);
}
export function AuthProvider({ children }) {
const [isLoggedIn, setIsLoggedIn] = useState(false);
function toggle() {
console.log("BOO!");
}
const value = {
isLoggedIn,
setIsLoggedIn,
};
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
This is my app.js file:
import React from "react";
import { Router } from "#reach/router";
import IndexPage from "./index";
import ProjectPage from "./project";
import { AuthProvider } from "../contexts/context";
const App = () => (
<AuthProvider>
<Router basepath="/app">
<IndexPage path="/" component={IndexPage} />
<ProjectPage path="/project" component={ProjectPage} />
</Router>
</AuthProvider>
);
export default App;
This is my index.js file:
import React, { useContext } from "react";
import { Link } from "gatsby";
import { useAuth } from "../contexts/context";
import { AuthContext } from "../contexts/context";
const IndexPage = () => {
console.log(useAuth())
return (
<div className="w-40 h-40 bg-red-400">
{/*<Link to="/project">to projects</Link>*/}
<div>Click me to toggle: uh</div>
</div>
);
};
export default IndexPage;
useAuth() should return the desired components and functions but instead is always returning undefined. I have looked over my previous code as well as snippets on stack overflow and I can't seem to find the correct fix.
The following includes code that successfully built and executed:
Original context.js
import '#stripe/stripe-js'
/* Functionality */
import React, { useContext, useEffect, useState } from "react";
import { navigate } from "#reach/router";
import firebase from 'gatsby-plugin-firebase';
import { useLocalStorage } from 'react-use';
const AuthContext = React.createContext()
export function useAuth() {
return useContext(AuthContext)
}
export function AuthProvider({ children }) {
const [isLoggedIn, setIsLoggedIn] = useState(false)
const [isLoading, setIsLoading] = useLocalStorage("loading", false);
// Sign In
const signInWithRedirect = (source) => {
let provider;
switch(source) {
case 'Google':
provider = new firebase.auth.GoogleAuthProvider()
break;
case 'Github':
provider = new firebase.auth.GithubAuthProvider()
break;
default:
break;
}
setIsLoading(true)
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.SESSION)
.then(() => {
// Existing and future Auth states are now persisted in the current
// session only. Closing the window would clear any existing state even
// If a user forgets to sign out.
// ...
// New sign-in will be persisted with session persistence.
return firebase.auth().signInWithRedirect(provider)
})
.catch((error) => {
// Handle Errors here.
let errorCode = error.code;
let errorMessage = error.message;
});
}
// Sign Out
const signOut = () => {
firebase.auth().signOut().then(() => {
// Sign-out successful.
setIsLoggedIn(false)
navigate('/app/login')
}).catch((error) => {
// An error happened.
});
}
useEffect(() => {
firebase.auth().onAuthStateChanged((user) => {
try {
// If user is authenticated
if (!!user) {
// Fetch firestore document reference
var docRef = firebase.firestore().collection("study_guide_customers").doc(user.uid)
docRef.get().then((doc) => {
console.log('checking doc')
// If the document doesn't exist, create it and add to the firestore database
if (!doc.exists) {
console.log('inside customer')
const customer = {
customerCreationTimestamp: firebase.firestore.Timestamp.now(),
username: user.displayName,
email: user.email
}
firebase.firestore().collection("study_guide_customers").doc(user.uid).set(customer)
.then(() => {
// After docuement for user is created, set login status
setIsLoggedIn(!!user)
setIsLoading(false)
})
.catch((error) => {
console.error("Error writing document: ", error);
});
// If document for user exists, set login status
} else {
setIsLoggedIn(!!user)
setIsLoading(false)
}
})
}
} catch {
console.log('Error checking firestore existence and logging in...')
}
})
}, [isLoggedIn, isLoading, setIsLoading, setIsLoggedIn])
const value = {
signOut,
isLoggedIn,
isLoading,
setIsLoading,
setIsLoggedIn,
signInWithRedirect,
}
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
)
}
Original app.js
/* Stripe Security */
import '#stripe/stripe-js'
/* Functionality */
import React from "react"
import { Router } from "#reach/router"
import PrivateRoute from "../components/PrivateRoute"
import Profile from "../components/Profile"
import Login from "../components/Login"
import Projects from "../components/Projects"
import IndexPage from "./index"
import NotFoundPage from './404'
import { AuthProvider } from "../contexts/context"
const App = () => (
<AuthProvider>
<Router basepath="/app">
<PrivateRoute path="/profile" component={Profile} />
<Login path="/login" component={Login}/>
<IndexPage path="/" component={IndexPage}/>
<Projects path="/projects" component={Projects} />
</Router>
</AuthProvider>
)
export default App
Original index.js
/* Stripe Security */
import '#stripe/stripe-js'
/* Functionality */
import * as React from "react"
import IndexContact from "../components/Index/Contact"
import IndexSelectedProjects from "../components/Index/SelectedProjects"
import IndexFeaturedProjects from "../components/Index/FeaturedProjects"
import IndexFooter from "../components/Index/Footer"
import IndexStudyGuide from "../components/Index/StudyGuide"
import IndexNavbar from "../components/Index/Navbar"
import IndexHeader from "../components/Index/Header"
import IndexAbout from '../components/Index/About'
import IndexExperience from '../components/Index/Experience'
import { useMount } from 'react-use';
const IndexPage = () => {
useMount(() => localStorage.setItem('loading', false));
return (
<>
<IndexNavbar />
<IndexHeader />
<IndexAbout />
<IndexExperience />
<IndexFeaturedProjects />
<IndexSelectedProjects />
<IndexStudyGuide />
<IndexContact />
<IndexFooter />
</>
)
}
export default IndexPage
Then in any component I could simply use the following code to access the context
import { useAuth } from "../contexts/context"
const { isLoggedIn, signInWithRedirect, isLoading } = useAuth()
Child components are mounted before parent. Fix your context.js file to add a default value for isLoggedIn state:
const defaultState = {
isLoggedIn: false,
setIsLoggedIn: () => {}
};
const AuthContext = React.createContext(defaultState);
Your defaultState should also include default methods for any parts of the context you wish to work with.

Testing a React with React-Router v.5, useHistory, useSelector and useEffect

I struggle with writing a proper test for a component that protects some routes and programatically redirects unauthorized users. The component looks like this:
import { useEffect } from 'react';
import { useSelector } from 'react-redux';
import { useHistory } from 'react-router-dom';
import { isAuthed } from 'redux/selectors/auth';
const mapState = state => ({
isAuthenticated: isAuthed(state),
});
const LoginShield = ({ children }) => {
const { isAuthenticated } = useSelector(mapState);
const history = useHistory();
useEffect(() => {
if (!isAuthenticated) {
history.push('/login');
}
}, [isAuthenticated]);
return children;
};
export default LoginShield;
I basically would like to check that the component redirects unauthenticated user and doesn't redirect an authenticated user (two basic test cases). I tried several approaches using Jest/Enzyme or Jest/ReactTestingLibrary and cannot find a good solution.
For now my test is a mess but I will share it so that someone can show me where the problem lays:
import React, { useEffect } from 'react';
import { act } from 'react-dom/test-utils';
import { mount } from 'enzyme';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import { Router } from 'react-router-dom';
import rootReducer from 'redux/reducers';
import LoginShield from 'components/LoginShield/LoginShield';
describe('LoginShield component', () => {
let wrapper;
let historyMock;
beforeEach(() => {
const initialState = { auth: { loginId: 'Foo' } };
const store = createStore(rootReducer, initialState);
historyMock = {
push: jest.fn(),
location: {},
listen: jest.fn(),
};
jest.mock('react-redux', () => ({
useSelector: jest.fn(fn => fn()),
}));
wrapper = mount(
<Provider store={store}>
<Router history={historyMock}>
<LoginShield>
<h5>Hello Component</h5>
</LoginShield>
</Router>
</Provider>,
);
});
it('renders its children', () => {
expect(wrapper.find('h5').text()).toEqual('Hello Component');
});
it('redirects to the login page if user is not authenticated', async () => {
await act(async () => {
await Promise.resolve(wrapper);
await new Promise(resolve => setImmediate(resolve));
wrapper.update();
});
// is the above necessary?
console.log(historyMock.push.mock.calls);
// returns empty array
// ... ?
});
it('doesn`t redirect authenticated users', () => {
// .... ?
});
});
Any tips are more than welcome! Thank you in advance. :)

Function not being called in ComponentDidMount

Not really sure how to move forward on this one. I have a (fairly) simple setup with react-router, almost everything works as expected - I get requests and responses between the app and my api - except I have a function I expect to run from my app's componentDidMount lifecycle function, but it's somehow not getting hit...
Here's a pretty minimal version of my code:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Switch } from 'react-router';
import { connect } from 'react-redux';
import { authenticate, unauthenticate } from './actions/session';
import RouteAuthenticated from './RouteAuthenticated'; // Route if authenticated
import RedirectAuthenticated from './RedirectAuthenticated'; // Redirect to login if not authenticated
import Home from './Home';
import Login from './Login';
import Signup from './Signup';
class App extends Component {
componentDidMount() {
const token = localStorage.getItem('token'); // eslint-disable-line no-undef
console.info(token); // <==== Shows a token, great!
console.info(this.props.authenticate.toString()); // Output below
if (token) {
console.info(`We have a token`); // <=== I get to here
this.props.authenticate(); // <=== Never gets run...????
} else {
this.props.unauthenticate();
}
}
render() {
const { isAuthenticated, willAuthenticate } = this.props;
const authProps = { isAuthenticated, willAuthenticate };
return (
<div className="App">
<Header />
<Switch>
<RouteAuthenticated exact path="/" component={Home} {...authProps} />
<RedirectAuthenticated exact path="/login" component={Login} {...authProps} />
<RedirectAuthenticated exact path="/signup" component={Signup} {...authProps} />
</Switch>
</div>
);
}
}
App.propTypes = {
authenticate: PropTypes.func.isRequired,
unauthenticate: PropTypes.func.isRequired,
isAuthenticated: PropTypes.bool.isRequired,
willAuthenticate: PropTypes.bool.isRequired
};
const mapStateToProps = (state) => ({
isAuthenticated: state.isAuthenticated,
willAuthenticate: state.willAuthenticate
});
const mapDispatchToProps = () => ({
authenticate,
unauthenticate
});
export default connect(mapStateToProps, mapDispatchToProps)(App);
And since this seemed super weird, I decided to go ahead and just make sure that there actually was a function to run. I put in the console.info(this.props.authenticate.toString()); line above, and here's what I get, which looks fine except that it never actually runs:
function authenticate() {
return function (dispatch) {
console.info("auth function");
dispatch({
type: 'AUTHENTICATION_REQUEST'
});
try {
console.info("AUTHing");
setCurrentUser(dispatch, _api2.default.get('is-auth'));
} catch (e) {
console.info("AUTH ERROR");
localStorage.removeItem('token'); // eslint-disable-line no-undef
window.location = '/login'; // eslint-disable-line no-undef
}
};
}
And here is the original function:
export const authenticate = () => (dispatch) => {
console.info("auth function"); // <=== never called?
dispatch({
type: 'AUTHENTICATION_REQUEST'
});
try {
console.info("AUTHing");
setCurrentUser(dispatch, api.get('is-auth'));
} catch (e) {
console.info("AUTH ERROR");
localStorage.removeItem('token');
window.location = '/login';
}
};
And the store:
import { createStore, applyMiddleware } from 'redux';
import { routerMiddleware } from 'react-router-redux';
import thunk from 'redux-thunk';
import history from './history';
import reducers from './reducers';
const middleWare = [thunk, routerMiddleware(history)];
const createStoreWithMiddleware = applyMiddleware(...middleWare)(createStore);
const store = createStoreWithMiddleware(reducers);
export default store;
Any ideas what I can try from here?

How to dynamically update a search term with React/Redux/Axios?

I'm building a small itunes application to fetch data from the Itunes api. I'm trying to implement the searchbar to make a relevant fetch to the server but I can't seem to figure out the correct props passed down from the Redux state. Since it is a small app I have used just the main App.js and index.js instead of separate folders for actions and reducers. My main App.js is as below:
import React, { Component } from 'react';
import './App.css';
import Navbar from './components/Navbar';
import Results from './components/Results';
import ToggleLayout from './components/ToggleLayout';
import AdditionalPages from './components/AdditionalPages';
import axios from 'axios';
import { connect } from 'react-redux';
const PATH_BASE = 'https://itunes.apple.com/search';
const PATH_TERM = 'term=';
const COUNTRY = 'country=es';
const ALBUMS = 'entity=album';
const LIMIT = 'limit=60';
class App extends Component {
constructor(props){
super(props);
}
}
render() {
return (
<div>
<Navbar
searchTerm={this.props.searchItunes.searchTerm}
onSearchChange={(e) => this.props.onSearchChange(e.target.value)}
fetchITunesAlbums={(e) => this.props.fetchITunesAlbums(e)}
/>
{ this.props.searchItunes.itunes &&
<Results itunes={this.props.searchItunes.itunes} grid={this.props.toggle.grid} additionalPages={this.props.toggle.additionalPages} fetchMorePages={this.fetchMorePages}/>
}
{ this.props.toggle.additionalPages &&
<AdditionalPages itunes={this.props.searchItunes.itunes} grid={this.props.toggle.grid}/>
}
<ToggleLayout
switchLayout={()=> this.props.switchLayout()}
grid={this.props.toggle.grid}
/>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
toggle: state.booleanReducer,
searchItunes: state.searchItunesReducer
};
};
const mapDispatchToProps = (dispatch) => {
return {
switchLayout: () => {
dispatch({
type:"GRID"
});
},
fetchMorePages: () => {
dispatch({
type:"ADDITIONALPAGES"
});
},
onSearchChange: (term) => {
dispatch({
type:"SEARCHTERM",
payload:term
});
},
fetchITunesAlbums: (e) => {
e.preventDefault();
axios.get(`${PATH_BASE}?${PATH_TERM}${searchTerm}&${COUNTRY}&${ALBUMS}&${LIMIT}`)
.then(response =>{
dispatch({
type: 'FETCHITUNESALBUMS',
payload: response.data
});
});
}
};
};
export default connect(mapStateToProps,mapDispatchToProps)(App);
So my issue is with my axios url. For example if I hard code the url such as
axios.get(`${PATH_BASE}?${PATH_TERM}&${'someband'}${COUNTRY}&${ALBUMS}&${LIMIT}`)
the I'm able to fetch the results from the server but not when I insert
axios.get(`${PATH_BASE}?${PATH_TERM}${searchTerm}&${COUNTRY}&${ALBUMS}&${LIMIT}`)
and below is my index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, combineReducers, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import logger from 'redux-logger';
const booleanReducer = (state = { grid:true, additionalPages:false }, action) => {
if (action.type === 'GRID'){
return state = {
...state,
grid:!state.grid
}
}
if (action.type === 'ADDITIONALPAGES'){
return state = {
...state,
additionalPages:!state.additionalPages
}
}
return state;
};
const searchItunesReducer = (state = { searchTerm:'', itunes:null }, action) => {
if (action.type === 'SEARCHTERM'){
return state = {
...state,
searchTerm:action.payload
}
}
if (action.type === 'FETCHITUNESALBUMS'){
return state = {
...state,
itunes: action.payload
}
}
return state;
}
const middleware = applyMiddleware(thunk, logger)
const store = createStore(combineReducers({booleanReducer,searchItunesReducer}),middleware);
console.log(store.getState());
store.subscribe(() =>{
console.log("store updated!", store.getState());
});
registerServiceWorker();
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
, document.getElementById('root'));
Any help is highly appreciated...
I think your issue lies with the search term, try to pass the value as a param,
<Navbar
searchTerm={this.props.searchItunes.searchTerm}
onSearchChange={(e) => this.props.onSearchChange(e.target.value)}
fetchITunesAlbums={(e) => this.props.fetchITunesAlbums(e,this.props.searchItunes.searchTerm)}
/>
and then the fetchItunes function as,
fetchITunesAlbums: (e,searchTerm) => {
e.preventDefault();
axios.get(`${PATH_BASE}?${PATH_TERM}${searchTerm}&${COUNTRY}&${ALBUMS}&${LIMIT}`)
.then(response =>{
dispatch({
type: 'FETCHITUNESALBUMS',
payload: response.data
});
});
}
};
};
Can you try this on the search bar
onChange={(e) => props.fetchItunesAlbums(e.target.value)}
And update your fetchItunesAlbums to:
axios.get(`${PATH_BASE}?${PATH_TERM}${e}&${COUNTRY}&${ALBUMS}&${LIMIT}`)
Instead of saving the search term on app state redux. see if it works.

React router redirect after action redux

I'm using react-redux and react-router. I need to redirect after an action is dispatched.
For example: I have registration a few steps. And after action:
function registerStep1Success(object) {
return {
type: REGISTER_STEP1_SUCCESS,
status: object.status
};
}
I want to redirect to page with registrationStep2. How can I do this?
p.s. In history browser '/registrationStep2' has not been visited. This page appears only after successful result registrationStep1 page.
With React Router 2+, wherever you dispatch the action, you can call browserHistory.push() (or hashHistory.push() if that’s what you use):
import { browserHistory } from 'react-router'
// ...
this.props.dispatch(registerStep1Success())
browserHistory.push('/registrationStep2')
You can do this from async action creators too if that is what you use.
Have you checked out react-router-redux? This library makes it possible to sync react-router with redux.
Here is an example from the documentation of how you can implement the redirection with a push action from react-router-redux.
import { routerMiddleware, push } from 'react-router-redux'
// Apply the middleware to the store
const middleware = routerMiddleware(browserHistory)
const store = createStore(
reducers,
applyMiddleware(middleware)
)
// Dispatch from anywhere like normal.
store.dispatch(push('/foo'))
Simplest solution for router version 4+:
We use "react-router-dom": "4.3.1"
It doesn't work with version 5+
export your browser history from the place where it was initialised
and use browserHistory.push('/pathToRedirect'):
Package history must be installed(example: "history": "4.7.2"):
npm install --save history
In my project I initialise browser history in index.js:
import { createBrowserHistory } from 'history';
export const browserHistory = createBrowserHistory();
Redirect in the action:
export const actionName = () => (dispatch) => {
axios
.post('URL', {body})
.then(response => {
// Process success code
dispatch(
{
type: ACTION_TYPE_NAME,
payload: payload
}
);
}
})
.then(() => {
browserHistory.push('/pathToRedirect')
})
.catch(err => {
// Process error code
}
);
});
};
To build on Eni Arinde previous answer's (I don't have the reputation to comment), here is how to use the store.dispatch method after an async action :
export function myAction(data) {
return (dispatch) => {
dispatch({
type: ACTION_TYPE,
data,
}).then((response) => {
dispatch(push('/my_url'));
});
};
}
The trick is to do it in the action files and not in the reducers, since reducers should not have side effects.
We can use "connected-react-router".
import axios from "axios";
import { push } from "connected-react-router";
export myFunction = () => {
return async (dispatch) => {
try {
dispatch({ type: "GET_DATA_REQUEST" });
const { data } = await axios.get("URL");
dispatch({
type: "GET_DATA_SUCCESS",
payload: data
});
} catch (error) {
dispatch({
type: "GET_DATA_FAIL",
payload: error,
});
dispatch(push("/notfound"));
}
};
};
Attention-- Please go to https://github.com/supasate/connected-react-router read the docs and set up the connected-react-router first, and then use "push" from connected-react-router.
You can use {withRouter} from 'react-router-dom'
Example below demonstrates a dispatch to push
export const registerUser = (userData, history) => {
return dispatch => {
axios
.post('/api/users/register', userData)
.then(response => history.push('/login'))
.catch(err => dispatch(getErrors(err.response.data)));
}
}
The history arguments is assigned to in the component as the a second parameter to the action creator (in this case 'registerUser')
An updated answer using hooks; for router v5 users.
Working on react-router-dom:5.1.2.
No installation of external package is required.
import { useHistory } from "react-router-dom";
function HomeButton() {
let history = useHistory();
function handleClick() {
history.push("/home");
}
return (
<button type="button" onClick={handleClick}>
Go home
</button>
);
}
You can use the history as you're previously used to.
More more details and APIs - read the manual
Here is the working copy of routing app
import {history, config} from '../../utils'
import React, { Component } from 'react'
import { Provider } from 'react-redux'
import { createStore, applyMiddleware } from 'redux'
import Login from './components/Login/Login';
import Home from './components/Home/Home';
import reducers from './reducers'
import thunk from 'redux-thunk'
import {Router, Route} from 'react-router-dom'
import { history } from './utils';
const store = createStore(reducers, applyMiddleware(thunk))
export default class App extends Component {
constructor(props) {
super(props);
history.listen((location, action) => {
// clear alert on location change
//dispatch(alertActions.clear());
});
}
render() {
return (
<Provider store={store}>
<Router history={history}>
<div>
<Route exact path="/" component={Home} />
<Route path="/login" component={Login} />
</div>
</Router>
</Provider>
);
}
}
export const config = {
apiUrl: 'http://localhost:61439/api'
};
import { createBrowserHistory } from 'history';
export const history = createBrowserHistory();
//index.js
export * from './config';
export * from './history';
export * from './Base64';
export * from './authHeader';
import { SHOW_LOADER, AUTH_LOGIN, AUTH_FAIL, ERROR, AuthConstants } from './action_types'
import Base64 from "../utils/Base64";
import axios from 'axios';
import {history, config, authHeader} from '../utils'
import axiosWithSecurityTokens from '../utils/setAuthToken'
export function SingIn(username, password){
return async (dispatch) => {
if(username == "gmail"){
onSuccess({username:"Gmail"}, dispatch);
}else{
dispatch({type:SHOW_LOADER, payload:true})
let auth = {
headers: {
Authorization: 'Bearer ' + Base64.btoa(username + ":" + password)
}
}
const result = await axios.post(config.apiUrl + "/Auth/Authenticate", {}, auth);
localStorage.setItem('user', result.data)
onSuccess(result.data, dispatch);
}
}
}
export function GetUsers(){
return async (dispatch) => {
var access_token = localStorage.getItem('userToken');
axios.defaults.headers.common['Authorization'] = `Bearer ${access_token}`
var auth = {
headers: authHeader()
}
debugger
const result = await axios.get(config.apiUrl + "/Values", auth);
onSuccess(result, dispatch);
dispatch({type:AuthConstants.GETALL_REQUEST, payload:result.data})
}
}
const onSuccess = (data, dispatch) => {
const {username} = data;
//console.log(response);
if(username){
dispatch({type:AuthConstants.LOGIN_SUCCESS, payload: {Username:username }});
history.push('/');
// Actions.DashboardPage();
}else{
dispatch({ type: AUTH_FAIL, payload: "Kullanici bilgileri bulunamadi" });
}
dispatch({ type: SHOW_LOADER, payload: false });
}
const onError = (err, dispatch) => {
dispatch({ type: ERROR, payload: err.response.data });
dispatch({ type: SHOW_LOADER, payload: false });
}
export const SingInWithGmail = () => {
return { type :AuthConstants.LOGIN_SUCCESS}
}
export const SignOutGmail = () => {
return { type :AuthConstants.LOGOUT}
}
signup = e => {
e.preventDefault();
const { username, fullname, email, password } = e.target.elements,
{ dispatch, history } = this.props,
payload = {
username: username.value,
//...<payload> details here
};
dispatch(userSignup(payload, history));
// then in the actions use history.push('/<route>') after actions or promises resolved.
};
render() {
return (
<SignupForm onSubmit={this.signup} />
//... more <jsx/>
)
}
while using react-router-dom version +5 you can't use useHistory hook in redux(redux toolkit).
So if you want to redirect after an action is dispatched you can get your history "via useHistory() hook" in your current page(component) then pass the history along with your payload as an argument to redux.
Hence you can easily manage your history in redux after an action is dispatched like this :
history.push ("somewhere)

Resources