I'm working on react native to build an application. for this reason i used react-router-redux but i get two crazy warning that it says:
Warning: ignores the history prop. to use a custom history , use 'import {Router}' instead of 'import{MemoryRouter as Router}'
this is my code:
my index.android.js:
import {AppRegistry} from 'react-native';
import Home from './app/components/Home';
AppRegistry.registerComponent('myCode', () => Home);
my Home:
import React, { Component } from 'react';
import { NativeRouter as Router, Route } from 'react-router-native';
import createHistory from 'history/createMemoryHistory';
import { Provider } from 'react-redux';
import { syncHistoryWithStore } from 'react-router-redux';
import configureStore from '../config/store';
import launch from './launch';
import Dinner from '../components/Dinner';
export default class Home extends Component {
render() {
const history = createHistory();
const store = configureStore(history);
const appHistory = syncHistoryWithStore(history, store);
return (
<Provider store={store}>
<Router history={appHistory}>
<Route path="/" component={launch}>
<Route path="dinner" component={Dinner} />
</Route>
</Router>
</Provider>
);
}
}
and this is my store.js:
import { applyMiddleware, createStore } from 'redux';
import { routerMiddleware } from 'react-router-redux';
import thunk from 'redux-thunk';
import promiseMiddleware from 'redux-promise-middleware';
import reducer from '../reducers/index';
export default function configureStore(history) {
const middleware = applyMiddleware(
promiseMiddleware(),
thunk,
routerMiddleware(history));
return createStore(reducer, {}, middleware);
}
i created a reducers which support all type of reducers:
const initialState = {
data: {},
error: null,
fetching: false,
fetched: false,
};
export default function reducer(state=initialState, action) {
switch (action.type) {
case (action.type.match(/^.*_PENDING$/) || {}).input:
// Action is pending (request is in progress)
return {...state, fetching: true};
case (action.type.match(/^.*_FULFILLED$/) || {}).input:
// Action is fulfilled (request is successful/promise resolved)
return {
...state,
data: action.payload.data,
fetched: true,
fetching: false};
case (action.type.match(/^.*_REJECTED$/) || {}).input:
// Action is rejected (request failed/promise rejected)
return {
...state,
error: action.payload,
fetched: false,
fetching: false
};
default:
return state;
}
};
By removing react-router-redux package and installing react-router-redux": "^5.0.0-alpha.6 the problem solved but the other thing that i care in this part is that this version doesn't have syncHistoryWithStore method so in this part i changed App like this:
import React, {Component} from 'react';
import {Route} from 'react-router-native';
import createHistory from 'history/createMemoryHistory';
import {Provider} from 'react-redux';
import {Switch} from 'react-router'
import {ConnectedRouter} from 'react-router-redux';
import configureStore from './store';
import Home from './components/Home';
import About from './components/About';
export default class App extends Component {
render() {
const history = createHistory();
const store = configureStore(history);
return (
<Provider store={store}>
<ConnectedRouter history={history}>
<Switch>
<Route path="/" component={Home}/>
<Route path="/about" component={About}/>
</Switch>
</ConnectedRouter>
</Provider>
);
}
}
Related
I have this error as Error: StateProvider(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null. And it is coming due to this section.
I am working on my project and using react and redux.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { StateProvider } from './StateProvider';
import { reducer, initialState } from './reducer';
ReactDOM.render(
<React.StrictMode>
<StateProvider initialState={initialState} reducer={reducer}>
<App />
</StateProvider>
</React.StrictMode>,
document.getElementById('root')
);
My App compo is like this
import './App.css';
import Header from "./Header";
import Home from "./Home";
import Checkout from "./Checkout";
import {BrowserRouter as Router, Switch} from "react-router-dom";
function App() {
return (
<Router>
<div className="app">
<Header/>
<Switch>
<Router path="/checkout">
<Checkout/>
</Router>
<Router path="/">
<Home/>
</Router>
</Switch>
</div>
</Router>
);
}
export default App;
And my reducer is like this
const initialState = {
basket : [],
};
const reducer = (state, action) => {
switch(action.type){
case "ADD_TO_BASKET":
return{
...state,
basket: [...state, action.item],
};
default:
return state;
}
};
export {reducer, initialState};
And my stateProvider is like this
import React, { createContext, useContext, useReducer } from "react";
export const StateContext = createContext();
export const StateProvider = ({ reducer, initialState, children}) => {
<StateContext.Provider value={useReducer(reducer, initialState)}>
{children}
</StateContext.Provider>
};
export const useStateValue = () => useContext(StateContext);
You are missing the return statement here:
export const StateProvider = ({ reducer, initialState, children}) => {
return (<StateContext.Provider value={useReducer(reducer, initialState)}>
{children}
</StateContext.Provider>)
};
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.
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'm trying to utilize the React router package in my React + Redux application, and doing so gives me the following errors:
Unexpected key "listings" found in previous state received by the reducer. Expected to find one of the known reducer keys instead: "routing". Unexpected keys will be ignored.
Uncaught TypeError: Cannot read property 'accounts' of undefined
ReactDOMComponentTree.js?1a2c:107Uncaught TypeError: Cannot read property '__reactInternalInstance$c5skqk6ty0f83o5hzvf0i19k9' of null
Here is my code:
initialState.js:
export default {
listings: {
status: '',
searchBy: '',
accounts: []
}
};
index.js (root reducer file):
import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';
const rootReducer = combineReducers({
routing: routerReducer
});
export default rootReducer;
routes.js:
import React from 'react';
import { Route, IndexRoute } from 'react-router';
import App from './components/App';
export default (
<Route path="/" component={App}>
<IndexRoute component={App}/>
</Route>
);
configureStore.js:
import {createStore, compose, applyMiddleware} from 'redux';
import rootReducer from '../reducers';
import reduxImmutableStateInvariant from 'redux-immutable-state-invariant';
import thunk from 'redux-thunk';
export default function configureStore(initialState) {
const store = createStore(rootReducer, initialState, compose(
// Add other middleware on this line...
applyMiddleware(reduxImmutableStateInvariant(), thunk),
window.devToolsExtension ? window.devToolsExtension() : f => f // add support for Redux dev tools
)
);
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('../reducers', () => {
const nextReducer = require('../reducers').default; // eslint-disable-line global-require
store.replaceReducer(nextReducer);
});
}
return store;
}
index.js (entry point for app):
import React from 'react';
import {render} from 'react-dom';
import { Provider } from 'react-redux';
import { Router, browserHistory } from 'react-router';
import routes from './routes';
import configureStore from './store/configureStore';
import initialState from './reducers/initialState';
import objectAssign from 'object-assign';
import mockTableData from './data/MockTableData';
import App from './components/App';
import { syncHistoryWithStore } from 'react-router-redux';
const listings = objectAssign({}, initialState.listings, {accounts: mockTableData});
const initial = objectAssign({}, initialState, {listings});
const store = configureStore(initial);
const history = syncHistoryWithStore(browserHistory, store);
render(
<Provider store={store}>
<Router history={history} routes={routes} />
</Provider>, document.getElementById('app')
);
Any ideas?
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)