I have reactjs setup with routes but my routing is not working. When I load the page it works but when I click on the links the URL changes but the component does not render. I tried to put as much as I can in the sandbox. load with URL/admin and click on logout etc.
https://codesandbox.io/s/o5430k7p4z
index
import React, { Component } from 'react'
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, combineReducers } from 'redux';
import { BrowserRouter, Route, browserHistory } from 'react-router-dom';
import promise from 'redux-promise';
import { createLogger } from 'redux-logger';
import App from './App'
import reducers from './reducers';
require("babel-core/register");
require("babel-polyfill");
import 'react-quill/dist/quill.snow.css'; // ES6
const logger = createLogger();
const initialState = {};
const createStoreWithMiddleware = applyMiddleware(promise)(createStore);
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
<BrowserRouter>
<App/>
</BrowserRouter>
</Provider>
, document.getElementById('root'));
App
import React, { Component } from 'react'
import { Switch, Route } from 'react-router-dom';
import ReactGA from 'react-ga';
ReactGA.initialize('UA-101927425-1');
import { connect } from 'react-redux';
import { fetchActiveUser } from './actions/index';
import { bindActionCreators } from 'redux';
import {getHttpRequestJSON} from './components/HTTP.js'
import Header from './components/header';
import Logout from './components/logout';
import SideBar from './components/sidebar';
import HomeContent from './containers/home';
import Ldapuser from './components/ldapuser';
import Admin from './components/admin/admin';
function fireTracking() {
ReactGA.pageview(window.location.pathname + window.location.search);
}
class App extends Component {
constructor(props){
super(props);
this.state = {
isGuest : false,
isSupp : false,
loading: true,
version: '',
};
}
initData = () => {
let self = this;
getHttpRequestJSON('/api/user/get/user/method/is/guest/format/json?quiet=1')
.then((response) => {
let isGuest = response.body.recordset.record.isGuest;
if(isGuest){
/*$(".logo").trigger('click');
//$("#overlay").show();
$('#modalIntro').modal('toggle');
$("#modalIntro").on("hidden.bs.modal", function () {
$(".logo").trigger('click');
});*/
}
self.props.isGuest = isGuest;
self.props.loading = false;
//self.props.version = response.header.version;
self.setState({
loading : false,
version : response.header.version,
isGuest : isGuest
});
})
.catch(error => {
console.log("Failed!", error);
//$('#myModalError .modal-body').html(error);
//$('#myModalError').modal('show');
});
getHttpRequestJSON('/api/user/get/user/method/is/supp/format/json?quiet=1')
.then((response) => {
self.setState({
isSupp : response.body.recordset.record.isSupp
});
})
.catch(error => {
console.log("Failed!", error);
//$('#myModalError .modal-body').html(error);
//$('#myModalError').modal('show');
});
}
componentDidMount() {
this.props.fetchActiveUser();
this.initData();
}
render() {
return (
<div>
<Header activeUser={this.props.activeUser} loading={this.state.loading} version={this.state.version} title={`Home`} />
<SideBar />
<main>
<Switch>
<Route path='/index.html' render={()=><HomeContent activeUser={this.props.activeUser} isGuest={this.state.isGuest} isSupp={this.state.isSupp} />} />
<Route path='/home' render={()=><HomeContent activeUser={this.props.activeUser} isGuest={this.state.isGuest} isSupp={this.state.isSupp} />} />
<Route path='/logout' component={Logout}/>
<Route path='/ldapuser' component={Ldapuser}/>
<Route path='/admin' render={()=><Admin isGuest={this.state.isGuest} isSupp={this.state.isSupp}/>} />
</Switch>
</main>
</div>
);
}
}
//export default App;
function mapStateToProps(state) {
if(state.activeUser.id > 0){
ReactGA.set({ userId: state.activeUser.id });
}
// Whatever is returned will show up as props
// inside of the component
return {
activeUser: state.activeUser
};
}
// Anything returned from this function will end up as props
// on this container
function mapDispatchToProps(dispatch){
// Whenever getUser is called, the result should be passed
// to all our reducers
return bindActionCreators({ fetchActiveUser }, dispatch);
}
//Promote component to a container - it needs to know
//about this new dispatch method, fetchActiveUser. Make it available
//as a prop
export default connect(mapStateToProps, mapDispatchToProps)(App);
The codesandbox is not working, but I think what is happening to you is a very common problem when using react-redux and react-router. The connect HOC of react-redux has a builtin SCU (shouldComponentUpdate), so for it to know to rerender is requires to receive new props. This can be done using the withRouter hoc of react-router. Simply wrap connect(..)(MyComponent) with withRouter(connect(..)(MyComponent)) or do it clean and use compose (from recomponse for example);
const enhance = compose(
withRouter,
connect(mapStateToProps)
)
export default enhance(MyComponent)
Make sure not to do it the other way around, because that does not work.
Related
The translation only work on refresh. It seems because i have used a wrapper around the App.js that why its not working.
Also i tried to add a key to intlprovider the translation worked but now all my inner components get refresh.
Could there be a way to used reactintl when using an app wrapper without refreshing all inner components??
Below you can find the app.js, index.js and the app wrapper:
Index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import './styles/global.css';
import registerServiceWorker from './registerServiceWorker';
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';
import rootReducer from './redux/rootReducers';
import AppWrapperContainer from './containers/appWrapperContainer/appWrapperContainer';
import {localeSet} from './redux/actions/localeActions';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(rootReducer, /* preloadedState, */ composeEnhancers(
applyMiddleware(thunk)
));
if(localStorage.H24Lang)
{
store.dispatch(localeSet(localStorage.H24Lang));
}
ReactDOM.render((
<Provider store={store}>
<AppWrapperContainer/>
</Provider>
),
document.getElementById('root'));
registerServiceWorker();
AppWrapperContainer.js
import React, { Component } from 'react';
import { IntlProvider, addLocaleData} from "react-intl";
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import messages from '../../messages';
import en from "react-intl/locale-data/en";
import fr from "react-intl/locale-data/fr";
import App from "../../App";
addLocaleData(en);
addLocaleData(fr);
class AppWrapperContainer extends Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
const {lang} = this.props
let locale =
(navigator.languages && navigator.languages[0])
|| navigator.language
|| navigator.userLanguage
|| lang
return (
// <IntlProvider locale={lang} messages={messages[lang]} key={lang}></IntlProvider>
<IntlProvider locale={lang} messages={messages[lang]} >
<App/>
</IntlProvider>
);
}
}
AppWrapperContainer.propTypes = {
lang: PropTypes.string.isRequired
}
//what reducer you need
function mapStateToProps(state) {
console.log("State is", state);
return {
lang: state.locale.lang
};
}
export default connect(mapStateToProps,null)(AppWrapperContainer);
App.js
import React, { Component } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import Home from './screens/home/home';
import { connect } from 'react-redux';
import { BrowserRouter as Router, Route,Redirect, Switch } from 'react-router-dom';
class App extends Component {
constructor(props) {
super(props);
this.state = {
};
}
componentWillMount() {
}
componentDidMount() {
}
render() {
return (
<div className="App">
<Router>
<div className = "app-main-content">
<Route exact path='/' component={Home} />
</Router>
</div>
);
}
}
//what reducer you need
function mapStateToProps(state) {
return {
};
}
function mapDispatchToProps(dispatch) {
return {
};
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
The key prop in IntlProvider forces React to remount the component (and all its children), but you just want them to be re-rendered (not remounted).
First confirm that your stored state is changing its locale/lang value as expected and that this change goes thougth mapStateToProps to your AppWrapperContainer component.
Then, make sure it is received in componentWillReceiveProps method and that a re-render is fired when its value changes. Then, all children will be re-rendered (if not blocked by shouldComponentUpdate method).
By the way, what is locale variable in AppWrapperContainer for?
I am unable to make the store available to children components.
The setup is a SPA with Symfony as back-end, though this should not make a difference for this matter.
The entry point for Webpack is the file:
/client/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware, compose } from 'redux';
import ReduxPromise from 'redux-promise';
import Root from './App';
import registerServiceWorker from './registerServiceWorker';
import reducers from './pages/combine_reducers';
let composeEnhancers = typeof(window) !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
const store = createStore(
reducers,
composeEnhancers(
applyMiddleware(ReduxPromise)
)
)
ReactDOM.render(
<Root store={store} />
, document.querySelector('#root')
);
registerServiceWorker();
The apps as such is at:
/client/App.js
import React from 'react';
import PropTypes from 'prop-types';
import { Provider } from 'react-redux';
import {
BrowserRouter as Router,
Route,
Link,
Switch
} from 'react-router-dom';
import HomePage from './pages/home/';
import AccountPage from './pages/account/';
const Root = ({ store }) => {
return(
<Provider store={store}>
<div className="App">
<header className="App-header">
<h1 className="App-title">Welcome to React</h1>
</header>
<Router>
<div>
<Link to="/account">Account</Link>
<Link to="/">Home</Link>
<div>
<Switch>
<Route path="/account" component={AccountPage} />
<Route path="/" component={HomePage} />
</Switch>
</div>
</div>
</Router>
</div>
</Provider>
)
}
Root.propTypes = {
store: PropTypes.object.isRequired
}
export default Root;
So far so good. The store is available in App.js.
But that's not the case at the next level. As you can see I'm attempting to make the store available using connect().
/client/pages/home/index.js
import React from 'react';
import { connect } from 'react-redux';
import Register from '../common/register/';
import PropTypes from 'prop-types';
class Home extends React.Component {
constructor(props){
super(props)
console.log(props);
}
render() {
return (
<div>
<h1> Hello World from home! </h1>
<Register />
</div>
);
}
}
Home.propTypes = {
store: PropTypes.object.isRequired
}
const mapStateToProps = (state) => {
return {
store: state.store,
}
}
export default connect(mapStateToProps)(Home)
At the lower level, the Register component, I'm able to submit the form, but the store not being available, I am unable to capture the response coming from the server.
/client/pages/common/register/index.js
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import RegisterForm from './containers/register';
import { actionSubmitRegister } from './actions/';
import PropTypes from 'prop-types';
class Register extends React.Component{
constructor (props) {
super(props);
this.state = {
registerResponse: '',
}
this.onSubmitRegister = this.onSubmitRegister.bind(this);
}
onSubmitRegister (event) {
event.preventDefault();
let submitForm = new Promise((resolve, reject) => {
actionSubmitRegister(this.props.form.RegisterForm.values);
});
submitForm.then((response) => {
console.log('response',response);
this.setState({registerResponse: this.props.submit_register.data});
console.log('registerResponse', this.state.registerResponse);
}).catch((error) => {
console.log(error);
});
}
render(){
return (
<div>
<div>
<RegisterForm
submitRegister={this.onSubmitRegister}
/>
<h3>{this.state.registerResponse}</h3>
</div>
</div>
)
}
}
/*
Register.propTypes = {
store: PropTypes.object.isRequired
}
*/
const mapStateToProps = (state) => {
return {
form: state.form,
submit_register: state.submit_register,
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators({actionSubmitRegister}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Register);
In mapStateToProps you map store: state.store but in general you use this method to map single props from your state to props in your component, not map the entire store (if this is even possible).
Eg:
form: state.form
The reason you are not able to access the store object in props is because you are not passing it down via props.
Provider from the react-redux library, makes it available to all children down the element tree. Store is made available via React's context API, NOT via props.
"Context is designed to share data that can be considered “global” for a tree of React components."
So in a child component of Provider, we can now do something like
render() {
const { store } = this.context;
console.log(store)
return(
...
)
}
This is the same way that react-redux's connect HOC is able to access the store and subsequently mapStateToProps or utilise the store's dispatch method to mapDispatchToProps.
Also I think Provider requires that it’s child element is a React component.
Check out this tutorial for a more in-depth explanation.
After the input I received above, I reviewed my code and got it to work.
Actually the main issue was on the /client/pages/common/register/index.js file, but I am posting the whole chain for reference:
/client/index.js
nothing to change
/client/App.js
The references to propTypes do not seem to be necessary, so I took them out.
import React from 'react';
import { Provider } from 'react-redux';
import {
BrowserRouter as Router,
Route,
Link,
Switch
} from 'react-router-dom';
import HomePage from './pages/home/';
import AccountPage from './pages/account/';
const Root = ({ store }) => {
return(
<Provider store={store}>
<div className="App">
<header className="App-header">
<h1 className="App-title">Welcome to React</h1>
</header>
<Router>
<div>
<Link to="/account">Account</Link>
<Link to="/">Home</Link>
<div>
<Switch>
<Route path="/account" component={AccountPage} />
<Route path="/" component={HomePage} />
</Switch>
</div>
</div>
</Router>
</div>
</Provider>
)
}
export default Root;
/client/pages/home/index.js
Here both propTypes and connect() do not seem to be required.
import React from 'react';
import Register from '../common/register/';
class Home extends React.Component {
constructor(props){
super(props)
}
render() {
return (
<div>
<h1> Hello World from home! </h1>
<Register />
</div>
);
}
}
export default Home;
/client/pages/common/register/index.js
The main issue here was the onSubmitRegister() method. The promise was not properly setup and I was referencing the action directly instead of using this.props. React do not seem to like that.
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import RegisterForm from './containers/register';
import { actionSubmitRegister } from './actions/';
class Register extends React.Component{
constructor (props) {
super(props);
this.state = {
registerResponse: '',
}
this.onSubmitRegister = this.onSubmitRegister.bind(this);
}
onSubmitRegister (event) {
event.preventDefault();
let submitForm = new Promise((resolve) => {
resolve(this.props.actionSubmitRegister(this.props.form.RegisterForm.values));
});
submitForm.then((result) => {
let data = result.payload.data;
this.setState({registerResponse: data.message});
}).catch((error) => {
console.log(error);
});
}
render(){
return (
<div>
<div>
<RegisterForm
submitRegister={this.onSubmitRegister}
/>
<h3>{this.state.registerResponse}</h3>
</div>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
form: state.form,
submit_register: state.submit_register,
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators({actionSubmitRegister}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Register);
I am getting this error -:
Invariant Violation: Could not find "store" in either the context or props of "Connect(Login)". Either wrap the root component in a , or explicitly pass "store" as a prop to "Connect(Login)"
My Index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './fonts/stylesheet.css';
import './index.css';
import Routes from "./routes";
import registerServiceWorker from './registerServiceWorker';
import reducer from './reducers';
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
const middleware = [ thunk ]
const store = createStore(
reducer,
applyMiddleware(...middleware)
)
ReactDOM.render(
<Provider store={store}>
<Routes />
</Provider>,
document.getElementById('root')
);
registerServiceWorker();
And Login.js is
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { setLoginStatusAndPartnerName } from '../actions';
import Header from './header';
import Footer from './footer';
class Login extends Component {
constructor() {
super();
this.state = {
emailId: "",
isError: false,
partner: "",
loginStatus: false
};
this.checkEmail = this.checkEmail.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange({ target }) {
this.setState({
[target.name]: target.value,
isError: false
});
}
render() {
return (
<div>
<div className="App">
<Header/>
<div className="login-container">
<form onSubmit={this.checkEmail}>
<div className="email-id">
<label>
Email Address
</label>
<input type="text" value={ this.state.emailId }
onChange={ this.handleChange } name="emailId"/>
</div>
</form>
</div>
</div>
<Footer/>
</div>
);
}
}
function mapStateToProps(state) {
const { loginStatus= false, partner='' } = state;
return{
loginStatus,
partner
}
}
function mapDispatchToProps(dispatch){
return {
sendLoginStatusAndInfo: (loginStatus , partner) => {
dispatch(setLoginStatusAndPartnerName(loginStatus , partner));
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Login);
My login.test.js
import React from 'react';
import ReactDOM from 'react-dom';
import { shallow, mount, render } from 'enzyme';
import Login from '../components/login';
describe('Login Component render', () => {
it('login renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<Login />, div);
});
it('checking whether the login form is present or not', () => {
expect(shallow(<Login />).exists(<form></form>)).toBe(true)
})
it('renders a label of login form', () => {
expect(shallow(<Login />).find('label').length).toEqual(1)
})
it('renders input box of login', () => {
expect(shallow(<Login />).find('input').length).toEqual(1)
})
it('renders the login information text', () => {
expect(shallow(<Login />).find('.login-info-text').length).toEqual(1)
})
});
I am not sure what i am doing wrong, but all the test cases are failing with the same error.
This is because you try to test connected Login component, but you probably should test raw component.
Add export to your component definition, like this:
export class Login extends Component
And import not default(connected ) but raw component to your test:
import {Login} from '../components/login';
Here are docs to read
import { HashRouter, Route, Switch } from 'react-router-dom';
ReactDOM.render((
<Provider store={store}>
<HashRouter history={history}>
<Switch>
<Route path="/" name="Home" component={Full} />
</Switch>
</HashRouter>
</Provider>
), document.getElementById('root'));
This is the best practise from the CoreUi Template.
Is it possible that routes at you re project does not return a component?
I try to write bellow code. but redux-thunk doesn't work.
Do you know how to resolve it?
When I exec this code, it can get this error.
createStore.js:113 Uncaught Error: Actions must be plain objects. Use custom middleware for async actions.
But I already installed redux-thunk. why does this error happen?
index.js
import { createDevTools } from 'redux-devtools';
import LogMonitor from 'redux-devtools-log-monitor';
import DockMonitor from 'redux-devtools-dock-monitor';
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import { Provider } from 'react-redux';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import { syncHistoryWithStore, routerReducer } from 'react-router-redux';
import thunk from 'redux-thunk';
import * as storage from './persistence/storage';
import randomToken from './utlis/random';
import * as reducers from './reducers';
import {
App,
Home
} from './components';
const reducer = combineReducers({
...reducers,
routing: routerReducer
});
if (!storage.get('token')) {
storage.put('token', randomToken());
}
const initialState = {
application: {
token: storage.get('token')
}
};
const DevTools = createDevTools(
<DockMonitor toggleVisibilityKey="ctrl-h" changePositionKey="ctrl-q">
<LogMonitor theme="tomorrow" preserveScrollTop={false} />
</DockMonitor>
);
const store = createStore(
reducer,
initialState,
compose(
applyMiddleware(
thunk
),
DevTools.instrument()
)
);
const history = syncHistoryWithStore(browserHistory, store);
ReactDOM.render(
<Provider store={store}>
<div>
<Router history={history}>
<Route path="/" component={App}>
<IndexRoute component={Home}/>
</Route>
</Router>
</div>
</Provider>,
document.getElementById('app')
);
Map.js
import React from 'react';
import { connect } from 'react-redux';
import * as actions from '../actions/cityForcast';
export class Home extends React.Component {
constructor (props, context) {
super(props, context);
}
componentWillMount () {
this.props.dispatch(actions.search(this.props.token));
}
render() {
return (
<div>
<button onClick={() => actions.search()}>Search</button>
</div>
);
}
}
export default connect(({ application, cityForecast }) => ({ application, cityForecast }))(Home);enter code here
cityForcast.js
export function search(token) {
return dispatch => {
console.log(token);
};
}
I could resolve that for myself. I needed to install redux-thunk as devDependencies as well.
I have issue with authorization.
I have SPA written in ReactJS with Redux.
I am using Axios for Ajax request and I want handle any ajax request via axios and use interceptor which can logout if response status is 401.
SPA stores info about authorization in redux store.
I don't know how to dispatch action from function which is not component.
Now I handle it with deleting cookie with token and hard page reload (I don't like this solution).
I can dispatch logout action from any component connected to redux store but not from function and I don't want handle status 401 in every request manually.
Could you help me?
here is some simplyfied code
myAjax.js
import axios from 'axios';
const ajax = axios.create({
baseURL: API_URL
});
ajax.interceptors.response.use(function (response) {
return response;
}, function (error) {
//here I want to dispatch action for logout
if(error.status == 401){
cookie.remove('auth', { path: '/' });
window.location.reload();
}
return Promise.reject(error);
});
export default ajax;
configureStore.js
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import rootReducer from './reducers.js';
export default function configureStore(initialState) {
return createStore(
rootReducer,
initialState,
applyMiddleware(
thunkMiddleware
)
)
}
reducers.js
import { combineReducers } from 'redux';
import userAuthorization from './reducer_auth';
import preferences from './reducer_preferences';
import productCatalogue from './reducer_product_catalogue';
import { reducer as formReducer } from 'redux-form';
const rootReducer = combineReducers({
form: formReducer,
preferences,
userAuthorization,
productCatalogue
});
export default rootReducer;
reducer_auth.js
import cookie from 'react-cookie';
import {
REQUEST_LOGIN,
RECEIVE_LOGIN_SUCCESS,
RECEIVE_LOGIN_FAILED,
DO_LOGOUT
} from '../actions/actions_auth';
const INITIAL_STATE = {
isAuthorized: false,
isLoggingIn: false
};
export default function(state = INITIAL_STATE, action){
switch(action.type){
case REQUEST_LOGIN:
return { ...state, isLoggingIn: true };
case RECEIVE_LOGIN_SUCCESS:
const { id_token, expires } = action.authorization;
let cookieConfig = {
path: '/'
};
if(expires){
cookieConfig.expires = new Date(expires * 1000);
}
cookie.save('auth', id_token, cookieConfig);
return { ...state, isLoggingIn: false, isAuthorized: true };
case RECEIVE_LOGIN_FAILED:
return { ...state, isAuthorized: false };
case DO_LOGOUT:
return { ...state, isAuthorized: false };
default:
return state;
}
}
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import { Router, browserHistory } from 'react-router';
import configureStore from './configureStore';
import routes from './routes';
const store = configureStore();
ReactDOM.render(
<Provider store={store}>
<Router history={browserHistory} routes={routes} />
</Provider>
, document.getElementById('app'));
routes.js
export default (
<Route>
<Route path="/" component={App}>
<IndexRoute component={Homepage} />
<Route path="settings" component={SettingsPage} />
<Route path="product_catalogue" component={ProductCatalgueList} />
<Route path="product_catalogue/new" component={ProductCatalgueAdd} />
<Route path="product_catalogue/:id" component={ProductCatalgueDetail}>
<Route path="edit" component={ProductCatalgueEdit} />
</Route>
</Route>
</Route>
);
app.js
import { connect } from 'react-redux';
import LoginPage from '../components/login_page';
import { doLogout } from '../../actions/actions_auth';
class App extends Component{
logout(){
const {dispatch} = this.props;
dispatch(doLogout());
}
render(){
const { isAuthorized } = this.props;
if(!isAuthorized) return <LoginPage />
return (
<div>
here i can dispatch logout, but not in ajax interceptor
<button onClick={this.logout.bind(this)}>Logout</button>
</div>
)
}
}
function mapStateToProps(state){
const { isAuthorized } = state.userAuthorization;
return {
isAuthorized
};
}
export default connect(mapStateToProps)(App);
action_auth.js
function doLogout(){
return dispatch => {
dispatch(
{
type: DO_LOGOUT
}
)
}
}
You have to pass the store to the myAjax component.
For example:
import axios from 'axios';
import { doLogout } from '../../actions/actions_auth';
const ajax = axios.create({
baseURL: API_URL
});
export function configureAjax(store) {
ajax.interceptors.response.use(function (response) {
return response;
}, function (error) {
if(error.status == 401){
cookie.remove('auth', { path: '/' });
// use the store to dispatch the logout action
store.dispatch(doLogout());
}
return Promise.reject(error);
});
}
export default ajax;
And in your application entry point just call the configureAjax function
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import { Router, browserHistory } from 'react-router';
import configureStore from './configureStore';
import routes from './routes';
import { configureAjax } from './myAjax'
const store = configureStore();
configureAjax(store); //Here you configure your ajax requests to use the store
ReactDOM.render(
<Provider store={store}>
<Router history={browserHistory} routes={routes} />
</Provider>
, document.getElementById('app'));
I ran into the same problem and here is my solution:
Set a listener in your app.jsx in method componentDidMount
Dispatch the customized event
Once the listener detect the customized event, dispatch the logout action;
app.js Code:
componentDidMount() {
window.addEventListener("logoutEvent", () => {
this.props.loginAction.clearUserInfo()
})
}
myAjax.js
const clearEvent = document.createEvent('HTMLEvents');
clearEvent.initEvent('logoutEvent', true, true);
document.dispatchEvent(clearEvent)