Here I have three components. Whenever I click the button in any of the child components (First
and Second
) the parent (Test
) component should display text entered into the child component. The problem is that whenever I click the button of other child component a new array is created with data from that particular component.
Here's the code:
Test component:
function Test() {
const [ChildData , setChildData] = useState('');
console.log(ChildData)
return (
<div>
<h1>Test</h1>
<div>
{ChildData? <div>{ChildData.map((data)=>{return <div>{data}</div> })}</div>:null }
</div>
<First passChildData={setChildData}/>
<Second passChildData={setChildData}/>
</div>
)
}
First component:
function First(props) {
const [Data, setData] = useState('');
const [Items, setItems] = useState('')
const handlechange = (e)=>{
setData(e.target.value)
}
const handleClick = ()=>{
if(Data != ''){
setItems((prevData)=>{
const newData = [...prevData,Data];
props.passChildData(newData)
return newData
})
setData('')
}else{
console.log("enter something")
}
}
return (
<div>
<h1>First</h1>
<input type="text" onChange={handlechange} value={Data}/>
<button onClick={handleClick}>Add</button>
{Data}
<div>{Items? <div>{Items.map((item)=>{return <div>{item}</div> })}</div>:null }</div>
</div>
)
}
Second component:
function Second(props) {
const [Data, setData] = useState('');
const [Items, setItems] = useState('')
const handlechange = (e)=>{
setData(e.target.value)
}
const handleClick = ()=>{
if(Data != ''){
setItems((prevData)=>{
const newData = [...prevData,Data];
props.passChildData(newData)
return newData
})
setData('')
}else{
console.log("enter something")
}
}
return (
<div>
<h1>Second</h1>
<input type="text" onChange={handlechange} value={Data}/>
<button onClick={handleClick}>Add</button>
{Data}
<div>{Items? <div>{Items.map((item)=>{return <div>{item}</div> })}</div>:null }</div>
</div>
)
}
What I expect is:
First
component;Second
component;First
componentSecond
componentI want "What is your name?" (Doesn't matter if it's in different divs or lines; I want every single item to be displayed in the order it was entered) to be shown inside the parent (Test
) component.
First, I would not have two components - First
and Second
- that are nearly identical except for their headings; I would have a single component with heading
as a prop.
Second, I would use a <form>
with an onSubmit
handler instead of a <button>
with an onClick
handler because this will allow the user to add an item by, for example, pressing [Enter] while the input field has focus.
Third, I would use the useEffect
hook to listen for changes in the First
component to the Items
array and then call the passChildData
prop function when there is a change. Note that variables that are to reference arrays should be initialized with empty arrays, for example: const [Items, setItems] = useState([])
.
The final code looks like:
function Test() {
const [ChildData, setChildData] = useState([]);
const setItem = (newItem) => {
setChildData([...ChildData, newItem]);
}
return (
<div>
<h1>Test</h1>
<div>
{ChildData.map((data, index) => <div key={index}>{data}</div>)}
</div>
<First heading="First" passChildData={setItem}/>
<First heading="Second" passChildData={setItem}/>
</div>
)
}
function First(props) {
const [Data, setData] = useState('');
const [Items, setItems] = useState([])
useEffect(() => {
props.passChildData(Data);
}, [Items])
const handlechange = (e)=>{
setData(e.target.value)
}
const handleSubmit = (e) => {
e.preventDefault();
if (Data === '') {
console.log('enter something');
} else {
setItems([...Items, Data]);
}
}
return (
<div>
<h1>{props.heading}</h1>
<form onSubmit={handleSubmit}>
<input type="text" onChange={handlechange} value={Data}/>
<button type="submit">Add</button>
{Data}
</form>
<div>
{Items.map((item, index) => <div key={index}>{item}</div>)}
</div>
</div>
)
}
I have created a fiddle for reference.