0

I have a userContext that is authenticating the user. It is working, but after successful login I want the user to be directed to the /dashboard. so I'm using this.props.history.push('/dashboard'); but I'm getting the below error.

TypeError: Cannot read property 'props' of undefined

Is there a better way to do this? While using a function?

UserContext.js

import React, {useState, createContext, useEffect} from 'react'
import Firebase from 'firebase'
import { Redirect } from 'react-router'

export const UserContext = createContext();


export const UserProvider = props => {
  const [user, setUser] = useState({
    username: "blank",
    loggendIn: false
  });


  var id = localStorage.getItem('id');
  // check if its null
  console.log(id);
  useEffect(() => {
    if (id != null) {
      console.log('id is there');
      // load user from realtime database
      const dbOBJ = Firebase.database().ref().child("users").child(id);
      dbOBJ.on('value', function(snapshot) {
        setUser(snapshot.val());
      });

    } else {
      console.log('no id :( ');
    }
    console.log(props.history);
    this.props.history.push('/dashboard');
  }, [id]);

  return (
    <UserContext.Provider value = {[user, setUser]} >
    {  props.children}
    </UserContext.Provider>
  );
}

app.js

import React from 'react';
import NavBar from './components/navBar';
import Login from './components/login';
import Session from './components/session';
import Dashboard from './components/dashboard';
import './App.css';
import Container from '@material-ui/core/Container';
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom';

import { UserProvider } from './model/UserContext'


function App() {

  return (
    <div>
      <UserProvider>
      <Session />
       <NavBar />
        <Container maxWidth="sm">
         <Router>
         <Route path='/Login' component={Login}  />
         <Route path='/Dashboard' component={Dashboard}  />
         </Router>
       </Container>
      </UserProvider>
    </div>
  );
}

export default App;
Chris Evans
  • 845
  • 1
  • 13
  • 29
  • Where does the `history` prop come from? It is usually injected via the `withRouter` HOC from react-router. – Drew Reese Sep 27 '19 at 05:15
  • Do you use 'withRouter' like this? export const UserProvider = props => withRouter({ – Chris Evans Sep 27 '19 at 05:21
  • no... more like `const UserProvider = withRouter((props => {...});` but to be honest it is easier to write your functional component normally, *then* wrap the export, like `export default withRouter(UserProvider);` – Drew Reese Sep 27 '19 at 05:24
  • this think this is the way to do it, but Im getting an error Attempted import error: 'UserProvider' is not exported from './model/UserContext'. Bit i am exporting the function ```const UserProvider = props => { ... }``` ```export default withRouter(UserProvider);``` – Chris Evans Sep 27 '19 at 05:33

2 Answers2

1

That is a functional component, there is no instance for this to exist on, history would just be on props. You also probably need to "inject" the router's history object into props as well.

import React, {useState, createContext, useEffect} from 'react'
import Firebase from 'firebase'
import { Redirect, withRouter } from 'react-router'

export const UserContext = createContext();


const UserProvider = ({  // don't export here
  children, // destructuring props here so it is clearer
  history
}) => {
  const [user, setUser] = useState({
    username: "blank",
    loggendIn: false
  });


  var id = localStorage.getItem('id');
  // check if its null
  console.log(id);
  useEffect(() => {
    if (id != null) {
      console.log('id is there');
      // load user from realtime database
      const dbOBJ = Firebase.database().ref().child("users").child(id);
      dbOBJ.on('value', function(snapshot) {
        setUser(snapshot.val());
      });
    } else {
      console.log('no id :( ');
    }
    console.log(props.history);
    history.push('/dashboard'); // <-- no this.props, just props or destructured props
  }, [id]);

  return (
    <UserContext.Provider value = {[user, setUser]} >
    { children }
    </UserContext.Provider>
  );
}

export default withRouter(UserProvider);
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • thanks for getting back to me so quickly, the issue when I do that is. its saying "TypeError: Cannot read property 'push' of undefined". Do i need to push props in the app.js? I'll add the code to the post. – Chris Evans Sep 27 '19 at 05:24
  • Updated to show using the router's `withRouter` HOC. – Drew Reese Sep 27 '19 at 05:30
  • I think this is the way to do it, but Im getting an error Attempted import error: 'UserProvider' is not exported from './model/UserContext'. But i am exporting the function const UserProvider = props => { ... } export default withRouter(UserProvider); do I need to add somthing to the app.js? – Chris Evans Sep 27 '19 at 05:39
  • Ah, I see, try either removing the `default` part from the export, or try removing the {} on the import. The issue is if you do a default export then the import would simple be `import UserProvider from './model/UserContext';`, or if you want to keep it a named export you'd have to do the `export const UserProvider = withRouter(props => ...` – Drew Reese Sep 27 '19 at 05:42
  • Yes, that fixed it :) but getting another error lol, ```Error: "Invariant failed: You should not use outside a "`` I moved to the top of the list in the return() of the App function in the app.js file. But still getting the same issue. – Chris Evans Sep 27 '19 at 05:58
  • This may be an issue with your design then, providers typically wrap app code, and routers typically live within the App. You may need to split the logic of "providing" and do the redirect to your dashboard in a "consumer", perhaps a specialized route or some component that can live within the `Router`. – Drew Reese Sep 27 '19 at 06:03
  • thanks, I'll look in to re-design and come back to this post. you have given we heaps of context though thank you so much for your help. – Chris Evans Sep 27 '19 at 06:24
0

I think you're mixing up multiple thing:

Fat arrow functions don't have a this on it defined it'll pick up window as this for your code.

window.name = 'Mamba';
(() => {console.log(this.name)})();

So you need to use the props directly here like this:

this.props.history.push('/dashboard'); => props.history.push('/dashboard');

You might like to go through this question if you're still unsuccessful

Black Mamba
  • 13,632
  • 6
  • 82
  • 105
  • I think your right, but I'm getting "history.push" us missing. Do I need to add this to the app.js? Is props.history.push( )built into react? or should I use something else to redirect? – Chris Evans Sep 27 '19 at 05:28