Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Passing an component state down the tree and to trigger top level rerender inside useEffect()

First new to ReactJS so bear with me. I have an app that I am attempting to sync using an external api. The structure looks something like

APP  ----> NAVBAR ---->MENU
       |            |->AVATAR 
       |
       ->MAIN -> HOME
              |->PROFILE --->PROFILE
                          |->LOGINHANDLER 

The loginhandler authenticates with the api server and obtains credentials which the profile compenent then uses to call an update and retrieve the users profile from the api server. The NAVBAR has the AVATAR which uses an image retrieved from the profile.

If the profile updater gets an updated image I need to trigger state update in the navbar to use the new profile image

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel


    function App() {
    const loggedIn = (localStorage.getItem('wp_user') !== null);
    const [seed,setSeed] = useState(0)
    const update =()=>{
        setSeed(Math.random);
    }
    return (
        <div className="App">
            <Navigation auth={loggedIn} />
            <Main onChange={update}/>
        </div>
    );
}

I know that using this is rerendering the whole app…which is not ideal.
Anyway I then pass the function down through each compontent via props until I hit the Profile component


export default function Profile(onChange){
    const [name,setName] = useState(getUserName())
    const [email,setEmail] =useState('');
    const [link,setLink] =useState('');
    useEffect(() => {
        new Api().getProfile().then(res=>{
            let update = false;
            if(res === null || res === undefined){
            } else {
                if (res.hasOwnProperty('name')) {
                    if (res['name'] !== name){
                        setName(res['name']);
                    }
                }
                if(res.hasOwnProperty('first_name')){
                    let fName = res['first_name'];
                    let sName = res['last_name'];
                    let updated = fName + " "+ sName;
                    if(updated !== name){
                        setName(updated);
                    }
                }
                if (res.hasOwnProperty('email')) {
                    if (res['email'] !== email){
                        setEmail(res['email']);
                    }
                }
                if (res.hasOwnProperty('link')) {
                    if (res['link'] !== link){
                        setLink(res['link']);
                    }
                }
                if (res.hasOwnProperty('avatar_urls')) {
                    if(getProfileUrl() !== res.avatar_urls['96']) {
                        setProfileUrl(res.avatar_urls['96'])
                        update = true;
                    }
                }
                if(update){
                    onChange();
                }
            }
        });
    }, [onChange]);
    return (
        <Card>
            <Typography component='a'  href={link} >
                Name: {name}
            </Typography>
            <Typography>
                Email: {email}
            </Typography>
        </Card>
    )

}

I have used profile like

  function UserWindow(onChange){
    return (
        <div>
            <header className="Login-Header">
                <h1>Profile</h1>
            </header>
            <Profile onChange={onChange}/>
            <AuthenticatorForm/>
        </div>
    );
}

and the Main window looks like

const Main = ({onChange}) => {
    return (
    <div className="App">
        <Routes>
            <Route path='/' element={<Home/>}></Route>
            <Route path='/User' element={<UserWindow onChange={onChange} />}></Route>
        </Routes>
    </div>
)}

I am getting the error…

ERROR onChange is not a function TypeError: onChange is not a function

[![enter image description here][1]][1]
[1]: https://i.stack.imgur.com/1L9r9.png

What is my mistake?

>Solution :

You need to de-structure the props in your UserWindow component like you did in Main. The parameter you define when you create a function component is the props object, so by saying {onChange} you are basically getting the onChange prop from the props object in a new variable.

If you instead just use onChange, you are basically naming your props object onChange. Here is a solution:

function UserWindow({onChange}){ // <-- here
    return (
        <div>
            <header className="Login-Header">
                <h1>Profile</h1>
            </header>
            <Profile onChange={onChange}/>
            <AuthenticatorForm/>
        </div>
    );
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading