React & Redux - state getting empty after route changed - javascript

I'm building a react & redux application and the problem I'm having is that after I do browserHistory.push('/myroute') and being successfully routed, I see that my state has been cleared, and while I need some data that's on the state from the previous route.. I still haven't found out if this is natural or not
My case is that I need to transfer data between routes.. I thought that is what state is for
this is my log:
action # 16:21:35.917 ON_ACTIVATE_FINISHED
counter.js:68 Object {counter: Object, registerReducer: Object, routing: Object}
core.js:97 action # 16:21:35.928 ##router/LOCATION_CHANGE
register.js:9 Object {}
core.js:97 action # 16:21:38.840 INPUT_PW_CHANGED
routes.js:
// #flow
import React from 'react';
import { Route, IndexRoute } from 'react-router';
import App from './containers/App';
import HomePage from './containers/HomePage';
import CounterPage from './containers/CounterPage';
import RegisterPage from './containers/RegisterPage';
export default (
<Route path="/" component={App}>
<IndexRoute component={CounterPage} />
<Route path="/counter" component={CounterPage} />
<Route path="/home" component={HomePage} />
<Route path="/register" component={RegisterPage} />
</Route>
);
CounterPage.js:
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import Counter from '../components/Counter';
import * as CounterActions from '../actions/counter';
function mapStateToProps(state) {
return {
counter: state.counter,
key: state.key
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(CounterActions, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
RegisterPage.js:
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import Register from '../components/Register';
import * as RegisterActions from '../actions/register';
function mapStateToProps(state) {
return {
pw: state.pw
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(RegisterActions, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Register);
reducers/counter.js:
import { Router, hashHistory } from 'react-router';
export const INPUT_KEY_CHANGED = 'INPUT_KEY_CHANGED';
export const ACTIVATE_KEY = 'ACTIVATE_KEY';
export const ON_ACTIVATE_FINISHED = 'ON_ACTIVATE_FINISHED';
export function onInputChanged(e) {
return {
type: INPUT_KEY_CHANGED,
data: e.target.value
};
}
export function activate() {
return {
type: ACTIVATE_KEY
};
}
export function onActivateFinished(json) {
return {
type: ON_ACTIVATE_FINISHED,
json
}
}
const SERVER_ADDRESS = 'http://*******:9001';
export const fetchActivationKey = () => (dispatch, getState) => {
var request = require('request-promise');
dispatch(activate())
const key = getState().counter.key;
return request(`${SERVER_ADDRESS}/invite/${key}`)
.then((fHost) => {
return request(`http://${fHost}:9000/api/start/${key}`)
})
.then(json => {
dispatch(onActivateFinished(json))
let currentState = getState();
if (currentState.counter.model.status === 0) {
hashHistory.push('/register');
}
});
}
Any ideas?

Based on https://github.com/reactjs/react-redux/blob/master/docs/api.md#provider-store and on my own app, it should look like this:
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
<Route path="/" component={App}>
<Route path="foo" component={Foo}/>
<Route path="bar" component={Bar}/>
</Route>
</Router>
</Provider>,
document.getElementById('root')
)
But I don't see the Provider and the Router in your code, the Provider is here to pass the main state to all components and without it I'm not sure it would work properly.
Normally, you can’t use connect() without wrapping the root component in Provider.

Let's have a try at this.
Take this code
.then(json => {
dispatch(onActivateFinished(json))
let currentState = getState();
if (currentState.counter.model.status === 0) {
hashHistory.push('/register');
}
});
and change it to this:
.then(json => {
dispatch(onActivateFinished(json))
let currentState = getState();
this.setState({counter: currentState.counter});
if (currentState.counter.model.status === 0) {
hashHistory.push('/register');
}
});
I'm still learning React and by no means am any kind of expert. So I am not 100% certain this will work. But it is an effort to assist based off of what I currently know.

Related

reactjs redux - user is null

I dont know why when i try to fetch data from api using redux (i can see the data in when i mapstatetoprops ) but this error (user is null ) message show up when i try to display data to user.
this is a screenshot shown the user is null error
I call the dispatch from componentDidMount react, i think its the right place to call api,
this is my code :
import React, { Component } from 'react';
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import { connect } from 'react-redux';
import store from './state/store';
import { loadUser } from './state/actions/auth/authActions';
//pages
import Login from './app/auth/Login';
import Wrapper from './wrapper/Wrapper';
class App extends Component {
componentDidMount() {
store.dispatch(loadUser());
}
render() {
const { token, user, isLoading } = this.props.auth
return (
<Router>
<Routes>
<Route path='/login' element={<Login />} />
<Route path='/' element={token ? <Wrapper user={user._id} isLoading={isLoading}></Wrapper> : <Login />} />
</Routes>
</Router>
)
}
}
const mapStateToProps = state => {
return {
auth: state.auth
}
}
export default connect(mapStateToProps, { loadUser })(App);

React Authentication using HOC

The server sends a 401 response if the user is not authenticated and I was trying to check for authentication in the front end using a HOC as seen in Performing Authentication on Routes with react-router-v4.
However, I am getting an error saying TypeError: Cannot read property 'Component' of undefined in RequireAuth
RequireAuth.js
import {React} from 'react'
import {Redirect} from 'react-router-dom'
const RequireAuth = (Component) => {
return class Apps extends React.Component {
state = {
isAuthenticated: false,
isLoading: true
}
async componentDidMount() {
const url = '/getinfo'
const json = await fetch(url, {method: 'GET'})
if (json.status !== 401)
this.setState({isAuthenticated: true, isLoading: false})
else
console.log('not auth!')
}
render() {
const { isAuthenticated, isLoading } = this.state;
if(isLoading) {
return <div>Loading...</div>
}
if(!isAuthenticated) {
return <Redirect to="/" />
}
return <Component {...this.props} />
}
}
}
export { RequireAuth }
App.js
import React from 'react';
import { BrowserRouter as Router, Route, Switch, withRouter } from 'react-router-dom';
import SignIn from './SignIn'
import NavigationBar from './NavigationBar'
import LandingPage from './LandingPage'
import Profile from './Profile'
import Register from './Register'
import { RequireAuth } from './RequireAuth'
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<Router>
<NavigationBar />
<Switch>
<Route exact path = '/'
component = {LandingPage}
/>
<Route exact path = '/register'
component = {Register}
/>
<Route exact path = '/profile'
component = {RequireAuth(Profile)}
/>
<Route path="*" component = {() => "404 NOT FOUND"}/>
</Switch>
</Router>
</div>
);
}
}
export default withRouter(App);
I can think of some possibilities:
------- Moved this to top which eventually fixed OP's issue -------
Try remove the curly braces at {React},
import React from 'react';
------- Moved this to top which eventually fixed OP's issue -------
In RequireAuth.js, Try
const RequireAuth = ({ Component }) => {} // changed from Component to { Component }
In App.js, use Component start with capital letter
<Route exact path = '/' Component = {LandingPage}/>
Also, in <Route path="*" component = {() => "404 NOT FOUND"}/>, looks like you're not passing in a React component because the function is not returning a JSX (I can't test now so I'm not very sure, though).
try this instead:
() => <div>404 NOT FOUND</div>
or if it doesn't work, define a functional component externally and pass into the Route:
const NotFoundComponent = () => <div>404 NOT FOUND</div>
<Route path="*" component = {NotFoundComponent}/>
try to do it like this:
const RequireAuth = ({ component: Component }) => {}

React Redux routing issues

I have recently experienced some issues with my react router and redux. Basically, I have a redux value set which let's me know if an item is selected. If the item is selected, then it will allow a URL to be used. One thing that I have noticed. If I add a redirect function. It breaks everything
Authentication function:
import React, { Component, Fragment } from "react";
import { Provider } from "react-redux";
// import store from "./store";
import {
BrowserRouter as Router,
Switch,
Route,
Redirect
} from "react-router-dom";
import { connect } from "react-redux";
import Profile from "./Profile";
import AddDomain from "./AddDomain";
import ChoosePackage from "./ChoosePackage";
import DashboardHome from "./DashboardHome";
import { Elements } from "#stripe/react-stripe-js";
import PropTypes from "prop-types";
import { loadStripe } from "#stripe/stripe-js";
const stripePromise = loadStripe("pk_test_7S0QSNizCdsJdm9yYEoRKSul00z4Pl6qK6");
class index extends Component {
componentDidMount() {
console.log("DOMAIN NAME" + this.props.domain_name);
}
state = {
domain_name: ""
};
static propTypes = {
domain_name: PropTypes.string.isRequired
};
domainCheck = () => {
if (this.props.domain_name != "") {
return <ChoosePackage />;
} else {
console.log("running rediect");
return <Redirect to="/dashboard" />;
}
};
render() {
return (
<React.Fragment>
<Route path="/dashboard/add-domain/choose-package">
{this.domainCheck()}
</Route>
<Route exact path="/dashboard/add-domain">
<AddDomain />
</Route>
<Route exact path="/dashboard/profile">
<Profile />
</Route>
<Route exact path="/dashboard">
<DashboardHome />
</Route>
</React.Fragment>
);
}
}
const mapStateToProps = state => ({
domain_name: state.domain.domain_name
});
index.defaultProps = {
domain_name: ""
};
export default connect(mapStateToProps, { pure: false })(index);
Any help is greatly appreciated

reactjs router not rendering components

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.

Access server props from within redux?

I'm trying to use redux, react-engine, and react-router.
The issue or question I have is that react-engine provides an object of props that come from the server. How do I access these props from within my ProvidedApp?
ProvidedApp.js
import React from 'react'
import { connect, Provider } from 'react-redux'
import App from './app'
import { mapStateToProps, mapDispatchToProps, store } from './redux-stuff'
// Connected Component
let ConnectedApp = connect(
mapStateToProps,
mapDispatchToProps
)(App)
let ProvidedApp = () => (
<Provider store={store}>
<ConnectedApp/>
</Provider>
)
export default ProvidedApp
Routes.js
import React from 'react'
import { Router, Route } from 'react-router'
import Layout from './views/Layout'
import App from './views/ProvidedApp'
module.exports = (
<Router>
<Route path='/' component={Layout}>
<Route path='/app' component={App} />
<Route path='/app/dev' component={App} />
</Route>
</Router>
)
I also think my configuration is a little wonky, I couldn't get Provider working any other way. If theres a way to have Provider wrap the Router I'd love to get that working.
Here's some code of what it looks like when I move Provider above Router
ConnectedApp.js
import React from 'react'
import { connect, Provider } from 'react-redux'
import App from './app'
import { mapStateToProps, mapDispatchToProps} from './redux-stuff'
let ConnectedApp = connect(
mapStateToProps,
mapDispatchToProps
)(App)
export default ConnectedApp
Routes.js
import React from 'react'
import { Provider } from 'react-redux'
import { Router, Route } from 'react-router'
import { store } from './redux-stuff'
import Layout from './views/Layout'
import App from './views/ConnectedApp'
module.exports = (
<Provider store={store}>
<Router>
<Route path='/' component={Layout}>
<Route path='/app' component={App} />
</Route>
</Router>
</Provider>
)
I get this error:
Could not find "store" in either the context or props of "Connect(App)". Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(App)".
Update
I found that I can access from he code in my first example within ProvidedApp. But I have no clue how I'm supposed to pass it into Redux.
let ProvidedApp = (props) => {
console.log(props)
return (
<Provider store={store}>
<ConnectedApp/>
</Provider>
)
}
Seems like I need to wrap the reducer and store and pass in the ServerProps to the default state like this.
let getDefaultState = (serverProps) => {
return {
'appName': serverProps.appName
}
}
let getReducer = (serverProps) => {
let defaultState = getDefaultState(serverProps)
return (state = defaultState, action) => {
}
}
let getStore = (serverProps) => {
let reducer = getReducer(serverProps)
return store = createStore(reducer)
}
let ConnectedApp = connect(
mapStateToProps,
mapDispatchToProps
)(App)
let ProvidedApp = (serverProps) => {
return (
<Provider store={getStore(serverProps)}>
<ConnectedApp/>
</Provider>
)
}
This is super ugly :/

Categories