I am working on a personal project in React for the first time. It has a structure of the following form (the following is simplified):
const Parent = () => {
const handleSubmit = (e) => {
e.preventDefault();
// Series of variables to hold the values from Child2
}
return (
<div>
<form onSubmit={handleSubmit}>
<div>
<Child1 name="child1"/>
<Child1 name="child2"/>
</div>
<button>Submit</button>
</form>
</div>
);
}
const Child1 = () => {
return (
<div>
<Child2 name="childchild1"/> // Child 2 components are individual inputs fields
<Child2 name="childchild2"/>
<Child2 name="childchild3"/>
</div>
);
}
const Child2 = () => {
return (
<div>
<input type="number" .......></input>
</div>
);
}
Basically put, I would like to add an import button in my Parent component which will fill the fields found in the Child2 Components. My question is: How would I go about passing the values from my Parent component to my Child2 Components so that they fill their input fields? I am looking for a relatively clean solution if possible. Thanks!
Component are build to be reusable. The clean way is to pass value from Child2 to Parent through Child 1.
const Parent = () => {
const [state1, setState1] = useState({})
const [state2, setState2] = useState({})
const handleSubmit = (e) => {
e.preventDefault();
// Series of variables to hold the values from Child2
}
return (
<div>
<form onSubmit={handleSubmit}>
<div>
<Child1 value={state1} onChange={setState1} name="child1"/>
<Child1 value={state2} onChange={setState2} name="child2"/>
</div>
<button>Submit</button>
</form>
</div>
);
}
const Child1 = ({ value, onChange }) => {
return (
<div>
<Child2 value={value.substate1} onChange={v=>onChange({...value, substate1: v})} name="childchild1"/> // Child 2 components are individual inputs fields
<Child2 value={value.substate2} onChange={v=>onChange({...value, substate2: v})} name="childchild2"/>
<Child2 value={value.substate3} onChange={v=>onChange({...value, substate3: v})} name="childchild3"/>
</div>
);
}
const Child2 = ({ value, onChange }) => {
return (
<div>
<input value={value} onChange={onChange} type="number" .......></input>
</div>
);
}
If that answer is unacceptable for you, you can have a look to redux or react context, but that breaks the reusability of the component and thats not "clean"
Related
I am working on a project with React for the first time. What I am trying to do is send a Child component's value that is within an input field to a variable in its Parent. More precisely, I would like the value to be transmitted whenever the value in the input field is changed. Here is a basic overview of what I have (simplified):
const Parent = () => {
const handleSubmit = (e) => {
e.preventDefault();
// Series of variables to hold the values from Child2
}
return (
<div>
<form onSubmit={handleSubmit}>
<div>
<Child1 name="child1"/>
<Child1 name="child2"/>
</div>
<button>Submit</button>
</form>
</div>
);
}
const Child1 = () => {
return (
<div>
<Child2 name="childchild1"/>
<Child2 name="childchild2"/>
<Child2 name="childchild3"/>
</div>
);
}
const Child2 = () => {
return (
<div>
<input type="number" .......></input>
</div>
);
}
In brief, my goal is to have the values from the input fields in the component Child2 to be transmitted to some array of values in Parent. That being said, I have looked at multiple ways of lifting up values in React online, but I haven't found something similar to my scenario. What would be the best way of proceeding? Thanks!
I think you can use useContext feature from react. so it is basically a way to manage state globally and you can share state between deeply nested components. so I'm thinking to make a function on the Parent that can be used from nested child to get value, you can use onChange method for instance
import { createContext, useContext } from "react";
const UserContext = createContext();
const Parent = () => {
const handleSubmit = (e) => {
e.preventDefault();
// Series of variables to hold the values from Child2
};
const handleInput = (data) => {
console.log(`this is data ${data.target.value}`);
};
return (
<UserContext.Provider value={handleInput}>
<div>
<form onSubmit={handleSubmit}>
<div>
<Child1 name="child1" />
<Child1 name="child2" />
</div>
<button>Submit</button>
</form>
</div>
</UserContext.Provider>
);
};
const Child1 = () => {
return (
<div>
<Child2 name="childchild1" />
<Child2 name="childchild2" />
<Child2 name="childchild3" />
</div>
);
};
const Child2 = () => {
const handleInput = useContext(UserContext);
return (
<div>
<input type="number" onChange={(e) => handleInput(e)}></input>
</div>
);
};
export default Parent;
perhaps this link can help you useContext
you can send a function from parent to child 1 then to child2, in this case we gonna send this function handleGetDataFromParent={getDataFromParent}
const Parent = () => {
const handleSubmit = (e) => {
e.preventDefault();
// Series of variables to hold the values from Child2
}
const getDataFromParent=(data)=>{
console.log('DATA from Child2', data)
}
return (
<div>
<form onSubmit={handleSubmit}>
<div>
<Child1 name="child1" handleGetDataFromParent={getDataFromParent}/>
<Child1 name="child2" handleGetDataFromParent={getDataFromParent}/>
</div>
<button>Submit</button>
</form>
</div>
);
}
const Child1 = ({handleGetDataFromParent}) => {
return (
<div>
<Child2 name="childchild1" handleGetDataFromParent={handleGetDataFromParent}/>
<Child2 name="childchild2" handleGetDataFromParent={handleGetDataFromParent}/>
<Child2 name="childchild3" handleGetDataFromParent={handleGetDataFromParent}/>
</div>
);
}
const Child2 = ({handleGetDataFromParent}) => {
const setNumber=(number)=>{
handleGetDataFromParent(number)
}
return (
<div>
<input type="number" onChange={(n)=>setNumber(n)}.......></input>
</div>
);
}
I want to create an Input component to be used to compose new form elements.
const Input = ({ value, children }) => {
const [currentValue, setCurrentValue] = useState();
return <div className='input'>
{children}
</div>
};
And my Text component would be:
const Text = (props) => {
return <Input {...props}>
<input
type='text'
value={/*how to bind to currentValue of Input*/}
onChange={/*how to call Input's setCurrentValue here*/}
/>
</Input>
}
I need to store currentValue state in parent, because I need to manage it for many different inputs.
Also I'm stuck at how to call parent's setCurrentValue on child's onChange method.
Any help is appreciated.
Update:
CodeSandbox
Update 2:
Real code from my repository
Solutions:
Context API
Pass props to children
Use children as funciton & pass relevant
Send that reference somehow using any method you see fit.
My preference: Composition with function
const Input = ({ value, children }) => {
const [currentValue, setCurrentValue] = useState();
const handlChange = (e) => {
setCurrentValue(e.target.value);
};
return <div className='input'>
{children(handlChange)}
</div>
};
const Text = (props) => {
return <Input {...props}>
{ (handleChange) => (
<input
type='text'
onChange = ( handleChange }
/>
) }
</Input>
}
Explanations:
How to pass props to {this.props.children}
https://victorofoegbu.com/notes/pass-props-to-react-children-faq
Please try like this.
// pass props to children.
const Input = ({ value, children }) => {
const [currentValue, setCurrentValue] = useState();
return <div className='input'>
{React.cloneElement(child, {onChange: setCurrentValue, value: currentValue}))}
</div>
};
// using props in childern.
const Text = (props) => {
return <Input {...props}>
{
({onChange, value})=> (
<input
type='text'
value={value}
onChange={(e)=>onChange(e.target.value)}
/>
)
}
</Input>
}
I'm having trouble passing data between two React siblings. I need to have an input component that will receive and pass the input to sibling that will handle the input and perform some logic.
I get to receive the data in the parent, but I cannot seem to get the passing to the sibling to work...
This is the wrapper/parent
const [corpBrandId, setCorpBrandId] = useState("");
const [secondInput, setSecondInput] = useState("");
const handleCorpBrandIdChange = ({ target }) => {
setCorpBrandId(target.value);
};
const handleSecondInputChange = ({ target }) => {
setSecondInput(target.value);
};
const handleClick = () => {
// console.log(corpBrandId);
// console.log(secondInput);
};
return (
<div className="App">
<InputField
label="Corporate brand ID "
onChange={handleCorpBrandIdChange}
value={corpBrandId}
/>
<TableConnect corpBrandId={corpBrandId} secondInput={secondInput} />
<InputField
label="Second input param"
onChange={handleSecondInputChange}
value={secondInput}
/>
<button onClick={handleClick}>Click me</button>
</div>
);
}
export default MainWrapper;
And here's the input component:
return (
<div>
<label>{label}</label>
<input type="text" value={value} name={name} onChange={onChange} />
</div>
);
};
export default InputField;
I've got no working or remotely close code how to receive the data...
This doesn't work in the sibling:
return (
<div>
Test
{props.corpBrandId}
{props.secondInput}
</div>
);
}
Anyone got any idea?
Based on your comment to #Shahar's answer, it sounds like you maybe haven't defined props in your TableConnect component. It should look something like this:
const TableConnect = (props) => {
return (
<div>
Test
{props.corpBrandId}
{props.secondInput}
</div>
);
};
Here's a codesandbox where TableConnect is correctly receiving the input from your input fields: https://codesandbox.io/s/throbbing-cloud-wgub8?file=/src/App.js
export default InputField;
return (
<div>
Test
{props.corpBrandInput} //shouldnt it be: corpBrandId
{props.inputText} //shouldnt it be: secondInput
</div>
);
}
What is the preferred way to change state in React using Hooks?
Option 1: I think this is the more "traditional"
function App() {
const [input, setInput] = useState('');
const onInputChange = (event) => {
setInput(event.target.value)
};
return (
<div className='App'>
<Form onInputChange={onInputChange} />
</div>
);
}
const Form = ({ onInputChange }) => {
return (
<div>
<div>
<div>
<input
type='text'
onChange={onInputChange}
/>
<button'>
Submit
</button>
</div>
</div>
</div>
);
};
export default Form;
Option 2: But I have seen people do this
function App() {
const [input, setInput] = useState('');
return (
<div className='App'>
<Form onInputChange={setInput} />
</div>
);
}
export default App;
const Form = ({ onInputChange }) => {
return (
<div>
<div>
<div>
<input
type='text'
onChange={(e) => {
onInputChange(e.target.value);
}}
/>
<button>
Submit
</button>
</div>
</div>
</div>
);
};
export default Form;
In Option 1 in the App component the logic to change it's state is there.
In Option 2, the logic to change App component is in a child component, right?
If I understand correctly, I think Option 1 makes more sense. What do you think?
option 1 is better because we only have to pass the props or data to child component not the logic , Logic must be reside in parent component.
I have a parent component that has a button with an onClick event and when there is an error I want to focus on an input that is in a child component. I know this can be done using useRef but I keep getting an undefined error. Here is my code:
/* Parent Component */
const parent = () => {
const acresRef = useRef();
const addrRef = useRef();
const acresFocus = () => {
acresRef.current.focus();
};
const addressFocus = () => {
addrRef.current.focus();
};
return (
<Child addrRef={addrRef} acresRef={acresRef} />
<button onClick={acresFocus} />
<button onCLick={addressFocus} />
)
}
/*Child Component*/
const Child = forwardRef(
({props}, acresRef, addrRef) => (
<div>
<label for="address">Address</label>
<input type="text" name="address" ref={addrRef} />
</div>
<div>
<label for="acres">Acres</label>
<input type="text" name="acres" ref={acresRef} />
</div>
)
);
You are incorrectly using refs here. forwardRef works when ref is passed to the component. Anything apart from that is a prop to the component. So in your case, acresRef and addrRef will be received within props by the child component.
/*Child Component*/
const Child = (props) => (
<>
<div>
<label htmlFor="address">Address</label>
<input type="text" name="address" ref={props.addrRef} />
</div>
<div>
<label htmlFor="acres">Acres</label>
<input type="text" name="acres" ref={props.acresRef} />
</div>
</>
);
/* Parent Component */
const Parent = () => {
const acresRef = React.useRef();
const addrRef = React.useRef();
const acresFocus = () => {
acresRef.current.focus();
};
const addressFocus = () => {
addrRef.current.focus();
};
return (
<>
<Child addrRef={addrRef} acresRef={acresRef} />
<button onClick={acresFocus}>acres</button>
<button onClick={addressFocus}>address</button>
</>
);
};
Check this codesandbox here.
You may also just bind the props like so :
const Child = (props) => {
const onClick = props.onClick;
return(<div><p>
{onClick()}
</p>
</div>
);
}
/* Parent Component */
const ParentEl = () => {
const onClick = ()=>{return "hi"};
return (
<div>
<Child onClick={onClick} />
</div>
)
}
/*Child Component*/
export default function App() {
return (
<div className="App">
<ParentEl />
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
or a codesandbox link to see directly what it does :
https://codesandbox.io/s/hidden-water-s8qjj?file=/src/App.js
here :
const onClick = props.onClick;
in the child component is doing all the magic