Search code examples
reactjsreact-hooksuse-effect

I want to navigate to a page/component where I passed the data from another component using both React hooks and useHistory, but I get undefined


I am new to react 3 weeks so far. After login into my dummy app, I fetch users from the database (useEffect) as a button. I want to click on a specific user and access his/her data on the next page. Therefore, I use the context API and the useHistory to respectively send the data (state) of the selected user to the component and usehistory to navigate to that component. However, when route to that component, I receive UNDEFINED for the data I made accessible to the component.

    import React, { useState, useEffect } from 'react';
    import { useHistory } from 'react-router-dom';
    import axios from 'axios';
    import { useToken } from '../auth/useToken';
    import { useUser } from '../auth/useUser';
    import { ClassDetails } from './ClassPages/ClassDetails';

    export const classSelectedContext = React.createContext();
    
    export const ClassSelection = () => {
    
        const user = useUser();
        const [token, setToken] = useToken();
        const [classesList, setClassesList] = useState([]);
        const [selectedClass, setSelectedClass] = useState('');
        const [className, setClassName] = useState('');
        const [startDate, setStartDate] = useState('');
        const [endDate, setEndDate] = useState('');
    
        const history = useHistory();
        
        const authStr = 'Bearer '.concat(token); 
    
        useEffect( () =>{
            axios.get('http://localhost:8081/user/instructor-classes',  {
                headers: {
                    'Authorization': authStr
                }
            })
            .then (res => {
                setClassesList(res.data);
            })
            .catch(
                err =>{
                    console.log(err);
                    history.push("/")
                }
            );
        
        },[selectedClass]);
        
        const AddNewClassClicked = () => {
            axios.post('http://localhost:8081/user/class', {
                className: className,
                startDate: startDate,
                endDate: endDate,
            }, {
                headers : {
                    'Authorization': authStr,
                    'Content-Type': 'application/json',
                }
            } )
            .then(
                //window.location.reload()
            )
            .catch(err => {
                console.log(err)
            });
       }
    
            const onSelectedClassClicked = () =>{
                  {history.push('class-detail')} 
            }
    
    
        return (
            <div className="content-container">
                <div className="content-container">
    
                    <h1>Your current classes</h1>
                        {classesList.map(item => (
                        <button 
                            value={item.classIdentifier}
                            onClick={(e) => {
                                setSelectedClass(e.target.value)
                                onSelectedClassClicked()
                            }}>
                            {item.className}
                        </button>
                        
                        )) } 
                    <classSelectedContext.Provider value={selectedClass} >
                        <ClassDetails />
                    </classSelectedContext.Provider>
                    
                </div>
    
                <div className="content-container">
                    <h1>Add a new Class</h1>
                    <input
                        value={className}
                        onChange={e => setClassName(e.target.value)}
                        placeholder="Class Name" />
                    <input
                        value={startDate}
                        onChange={e => setStartDate(e.target.value)}
                        placeholder="Start Date of the Class" /> 
                    <input
                        value={endDate}
                        onChange={e => setEndDate(e.target.value)}
                        placeholder="End Date of the Class" />               
                    
                    <button
                        disabled={
                            !className || !startDate ||
                            !endDate 
                        }
                    onClick={AddNewClassClicked}>add new Class</button>
                </div>
            </div>
        )
    
    }

So, After the click of the button, I am using history to get to ClassDetails (class-detail) where I can get the data passed to that component through the useEffect hook.

    import React from 'react';
    import { useState, useEffect, useContext } from 'react';
    import { classSelectedContext } from '../ClassSelection';
    import axios from 'axios';
    import { useHistory } from 'react-router-dom';
    import { useToken } from '../../auth/useToken';

    export const ClassDetails = () => {
        
        const [token, setToken] = useToken();
    
        const history = useHistory();
        const authStr = 'Bearer '.concat(token); 
        let context = useContext(classSelectedContext);
    
    
        useEffect( () =>{
            console.log(context)
        
            axios.get(`http://localhost:8081/user/class/${context}`,  {
                headers: {
                    'Authorization': authStr
                }
            })
            .then (res => {  
                console.log(res.data);
            })
            .catch(
                err =>{
                    console.log(err);
                    console.log("An error happenned")
                }
            );
         }, []);
       
        return (
            <div>
                welcome {context}
                <div>
                    { GetClassDetail(context) }
                </div>
                {/* <classSelectedContext.Consumer>
                    {
                        
                        variable => { GetClassDetail(variable)}
                    }
                </classSelectedContext.Consumer> */}
           </div>
        )
    }

At last, these are my routes

    import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
    import { LogInPage } from './pages/LogInPage';
    import { SignUpPage } from './pages/SignUpPage';
    import { UserInfoPage } from './pages/UserInfoPage';
    import { PrivateRoute } from './auth/PrivateRoute';
    import { ClassSelection } from './pages/ClassSelection';
    import { WelcomePage } from './pages/WelcomePage';
    import { UserLogIn } from './pages/UserLogIn';
    import { ClassDetails } from './pages/ClassPages/ClassDetails';

    export const Routes = () => {
        return (
            <Router>
                <Switch>
                    <PrivateRoute path="/class-detail" exact>
                        <ClassDetails />
                    </PrivateRoute> 
                    <PrivateRoute path="/instructor-classes" exact>
                        <ClassSelection />
                    </PrivateRoute>
                    <PrivateRoute path="/select-school" exact>
                        <UserInfoPage />
                    </PrivateRoute>
                    <Route path="/user-login">
                        <UserLogIn />
                    </Route>
                    <Route path="/teacher-login">
                        <LogInPage />
                    </Route>
                    <Route path="/">
                        <WelcomePage />
                    </Route>                
                    <Route path="/signup">
                        <SignUpPage />
                    </Route>
                </Switch>
            </Router>
        );
    }

Solution

  • Please check the code in this codesandbox link. I have tried to create a small application you are trying to make. I have added a wrapper of context to the router so that it will be available with route change. you can use render prop also and add provider with individual component like the below code.

    <Route path="/" render={(props) => (<ContextB><Component2 {...props}/></ContextB>)}/>

    Also history will work only if the component is wrapped inside "react router dom" in your code may be you are handling.