The state is being mutated directly in the parent's onChange and hence your useEffect in ReadOnly component is not being triggered.
Update your onChange
const onChange = (e: IChangeEvent<any>) => {
setFormDataAndIncludings(prev => ({
...prev ,
queryData : [
e.formData,
...prev.queryData.slice(0,1)
]
}))
//const newFormDataAndIncludings = { ...formDataAndIncludings };
//newFormDataAndIncludings.queryData[0] = e.formData;
//setFormDataAndIncludings(newFormDataAndIncludings);
};
Provide useEffect dependency as usual
React.useEffect(() => {
setValue(firstName());
}, [formContext]);
Answer from Guru Hadadi on Stack OverflowFirst off, I am painfully aware this is not an ideal scenario. I have an app using OpenUI5 with some React functional components/hooks inside:
private _myReactWindow = window["studioStepComponents"];
private _myReactView;
private someFunction(someNewValue) {
this._myReactView.someProp = someNewValue
}
private someRenderFunction() {
this._myReactView = this._myReactWindow.createElement(this._myReactWindow.MyReactComponent,
{
someProp: this.someValue
});
} createElement is working fine and in "someFunction" I can see the correct values for this._myReactView.someProp, even after setting it to a different value, but the useEffect() hook is not triggering on the React side. Is there any way I can get it to pick up the change? I realize these are different frameworks with different bindings but with the value changing I still would have thought React would see the prop change.
"use client";
import { useEffect, useState } from "react";
import Header from "./header";
export default function Layout({ children }: { children: React.ReactNode }) {
useEffect(() => { console.log('changed'); }, [children]);
return (
<>
<Header />
<div>{children}</div>
</>
);
}Try to make sure your photos prop is immutable,
meaning, you send a new array each time there is a change and not mutating the array
This may not be the most correct answer, but I've tried this and it works. Hat tip to https://dev.to/stephane/how-to-make-sure-useeffect-catches-array-changes-fm3
useEffect(() => {
console.log("trigger");
Promise.all(
photos.map(async photoId => {
const url = await getImage(photoId);
return url;
})
)
.then(photoUrls => {
setPhotos(photoUrls);
})
.catch(e => console.log(e));
}, [JSON.stringify(photos])); // stringify the array used as the trigger, and it'll catch the change
Here's my App.js component
App.js
function App() {
return (
<GithubProvider>
<AlertProvider>
<Router>
<div className="flex flex-col justify-between h-screen">
<Navbar />
<main className='container mx-auto px-3 pb-12'>
<Alert />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/user/:login" element={<User />} /> {/* ISSUE */}
<Route path="*" element={<NotFound />} />
</Routes>
</main>
<Footer />
</div>
</Router>
</AlertProvider>
</GithubProvider>
);
}The route change to the user component happens in Home Component, I search for a github profile,
a lift of profiles are displayed, I click on the visit profile button on the profile card, which Links me to User component
UserItem.js:
import { Link } from 'react-router-dom'
import PropTypes from 'prop-types'
const UserItem = ({user: {login, avatar_url}}) => {
return (
<div className='card shadow-md compact side bg-base-600'>
<div className='flex-row items-center space-x-4 card-body'>
<div>
<div className='avatar'>
<div className='rounded-full shadow w-14 h-14'>
<img src={avatar_url} alt='Profile' />
</div>
</div>
</div>
<div>
<h2 className='card-title'>{login}</h2>
<Link
className='text-base-content text-opacity-60'
to={`/user/${login}`}
>
Visit Profile {/* TRIGGERING HERE!!!!!!!!!!! */}
</Link>
</div>
</div>
</div>
)
}
UserItem.propTypes = {
user: PropTypes.object.isRequired,
}
export default UserItem;My user Component where the function getUser needs to be triggered->
The function getUsers works fine as when i used it outside an useEffect, it did give me the right response from the fetch call inside.
User.js
import React, { useEffect, useContext } from 'react'
import {FaCodepen, FaStore, FaUserFriends, FaUsers} from 'react-icons/fa'
import { Link } from 'react-router-dom'
import GithubContext from '../context/github/GithubContext'
import { useParams } from 'react-router-dom'
import Spinner from '../components/layout/Spinner'
import RepoList from '../components/repos/RepoList'
export const User = () => {
const params = useParams()
const { getUser, user, loading, getUserRepos, repos } =
useContext(GithubContext)
console.log("params", params, "login", params.login) // WORKS FINE
useEffect ( () => {
console.log("use-effect", getUser, "", params.login)
getUser(params.login)
getUserRepos(params.login)
},[params.login]) // DOESNT TRIGGER WITH [] or [params.login]
console.log( "user-check",user) // user object empty
const {
name,
type,
avatar_url,
location,
bio,
blog,
twitter_username,
login,
html_url,
followers,
following,
public_repos,
public_gists,
hireable,
} = user
console.log("YES THIS PAGE") // Here I was checking whether page is being mounted and YES it is being mounted
const websiteUrl = blog?.startsWith('http') ? blog : 'https://' + blog
if(loading){
return <Spinner />
}
return (<> TYPICAL INFORMATION DISPLAY UI </>)
}You are calling setState before it is defined. For the initial value, you just return it without calling setState. For having the second dropdown state depending on the first one's state, you could use the useEffect hook, like so:
const [firstDropdown, setFirstDropdown] = useState(getInitialState({type:"", options:""}));
const [secondDropdown, setScondDropdown] = useState(0); // you could use whatever initial value you want here
const getInitialState= () => {
//some logic to get the value in type
//just return that inital value without calling setState
}
// the function inside useEffect runs every time firstDropdown changes
useEffect(()=>{
// some logic on firstDropdown
let value = firstDropdown+25
setScondDropdown(value)
},[firstDropdown]);
Update:
In your useEffect, change this :
setOptions(prevOptions => ({
...prevOptions,
["options"]: updatedOptions
}));
to this :
setOptions(updatedOptions)
Use the useState as below:
const [state, setState] = useState({})
setState((prev) => {
// do whatever you want to do with the previous state
})
And for the name, please do not use setState as your setter function of useState. because setState is another method in the class based components in React
The array you pass as second argument to useEffect only checks if the elements in the array are === to the elements in it in the previous render. const newArr = arr; will lead to newArr === arr since it doesn't create a new array, which is not what you want.
Create a new array with all the elements in arr and it will work as expected.
const App = props => {
const { arr, setArr } = useContext(GlobalContext)
const handleChange = () => {
const newArr = [...arr]
[10, 20, 30, 40].forEach(v => {
newArr.push(v)
})
setArr(newArr)
}
return <>{/* ... */}</>
}
When you want to update array using useState hook. Make sure to spread the array into new array and update the new array so that your useEffect listening for this state will be called.
UseEffect will not call in the below code snippet as you are directly updating array.
const [skills, selectedSkills] = useState([])
const onSelect = (selectedList) => {
selectedSkills(selectedList)
}
useEffect(() => {
MyLogger('useEffect called')
}, [skills])
UseEffect will call in the below code snippet as we are keeping new reference to the array.
const [skills, selectedSkills] = useState([])
const onSelect = (selectedList) => {
const tempSelectedList = [...selectedList]
selectedSkills(tempSelectedList)
}
useEffect(() => {
MyLogger('useEffect called')
}, [skills])