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
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…
[![enter image description here][1]][1] [1]: https://i.stack.imgur.com/1L9r9.png
ERROR onChange is not a function TypeError: onChange is not a function
What is my mistake?
2
Answers
You’ve got a bunch of syntax errors to check through. hint there is to look at the props you’ve forgottne to destructure (onChange) => ({ onChange })
But I would highly recommend learning about context providers for passing state around different components, your approach will get complicated quickly.
old doc – https://legacy.reactjs.org/docs/context.html
new doc – https://react.dev/reference/react/createContext
You need to de-structure the props in your
UserWindow
component like you did inMain
. The parameter you define when you create a function component is theprops
object, so by saying{onChange}
you are basically getting theonChange
prop from the props object in a new variable.If you instead just use
onChange
, you are basically naming your props objectonChange
. Here is a solution: