I am trying to pass onChange input value from child component to parent component in react js. I pass with props. But in the component, it writes value as location: <input />. As I understand it return value as object but when I try to convert with Json.stringfy it returns an error. So how can pass and set this value in parent component?
class Search extends Component {
// Define Constructor
constructor(props) {
super(props);
render() {
return (
<div>
<Script url="https://maps.googleapis.com/maps/api/jskey=Key&libraries=places"
onLoad={this.handleScriptLoad}
/>
<input onChange={(e)=>this.props.handleChangeSearch(e)} defaultValue={this.props.location} id="autocomplete" placeholder="search city..."
style={{
margin: '0 auto',
maxWidth: 800,
}}
/>
</div>
);
}
}
export default Search;
Main Component
class MainPage extends Component {
constructor(props) {
super(props);
this.state = {
location: ""
}
}
componentDidMount(){
this.callLoc();
}
handleChangeSearch=(event)=> {
// console.log(JSON.stringify(event.target)+" event");
this.setState({location: event.target});
}
render() {
return (
<div id="main">
<Script url="https://maps.googleapis.com/maps/api/js?key=your_api_key&libraries=places"
onLoad={this.handleScriptLoad} />
<h3 id="mainText">Choose Your Next Destination</h3>
<div id='card'>
<div class="input-group">
<Search handleChangeSearch={this.handleChangeSearch} location={this.state.location}/>
<Button onClick={this.searchLoc}>Search</Button>
</div>
<br></br>
<Button onClick={()=>this.callLoc()} block>Near by Guides</Button>
</div>
</div>
);
}
event.target will just point to the element that generated this event, you need to use event.target.value to get the value of the input.
React uses synthetic events (or read here), so passing the event object can lead to a stale object.
// Pass the value and not the event object
<input onChange={ (e) => this.props.handleChangeSearch(e.target.value) } />
// Fix the handler
handleChangeSearch = (value) => { ... }
Related
I am simply trying to get the text from the input field for which handler function is attached to a button and then just trying to store in input value into the state object.
But the state object doesn't store that value
class EditTextArea extends React.Component {
constructor() {
super();
this.state = {
message: "",
};
this.handleButton = this.handleButton.bind(this);
}
handleButton(e) {
const text = e.target.previousElementSibling.value;
this.setState({ message: text });
console.log(this.state);
}
render() {
return (
<div>
<form>
<input type="text" name="customText" />
<button type="button" onClick={this.handleButton}>
Send
</button>
</form>
</div>
);
}
}
ReactDOM.render(<EditTextArea />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
Nothing in your render is using this.state.message, so you're not going to see anything happen. (Note that if you're wondering about the console.log of state, see this question's answers; the update is asynchronous.)
If you actually use message, you see that it works:
class EditTextArea extends React.Component {
constructor(props) {
super(props);
this.state = {
message: "",
};
this.handleButton = this.handleButton.bind(this);
}
handleButton(e) {
const text = e.target.previousElementSibling.value;
this.setState({ message: text });
}
render() {
return (
<div>
<div>
Message is: {this.state.message}
</div>
<form>
<input type="text" name="customText" />
<button type="button" onClick={this.handleButton}>
Send
</button>
</form>
</div>
);
}
}
ReactDOM.render(<EditTextArea />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
Also note that you need to accept a parameter in your constructor and pass it to super(); see above. That wasn't the problem, but it still still incorrect.
That said, I wouldn't do it that way. If you want an uncontrolled input, use a ref to access its value so you don't have to do the DOM traversal, which is easily broken with a small change to render:
class EditTextArea extends React.Component {
constructor(props) {
super(props);
this.state = {
message: "",
};
this.handleButton = this.handleButton.bind(this);
this.inputRef = React.createRef(null);
}
handleButton() {
const text = this.inputRef.current.value;
this.setState({ message: text });
}
render() {
return (
<div>
<div>
Message is: {this.state.message}
</div>
<form>
<input type="text" name="customText" ref={this.inputRef} />
<button type="button" onClick={this.handleButton}>
Send
</button>
</form>
</div>
);
}
}
ReactDOM.render(<EditTextArea />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
Or you might consider a controlled component.
More about controlled vs. uncontrolled components hrere. More about refs here.
I run your script so initially, your message state taking an empty state just add a text and click a couple of times so Your message state is updating the second time.
So if you need to dynamic change on input so I suggest you right an input handler and call it into the input change function and handle it separately.
onInputchange(e) {
this.setState({
[e.target.name]: e.target.value
});
}
I tried your code and it actually worked. But the problem is you cant change state and get changed state in same function, it reads the previus state. Just try to click button 2 times, you will see it works
In react states are updated asynchronously. To check your updated state you can check it by logging in render or in react developer tools.
According to you code you can check code given below
class EditTextArea extends React.Component {
constructor() {
super();
this.state = {
message: "",
};
this.handleButton = this.handleButton.bind(this);
}
handleButton(e) {
const text = e.target.previousElementSibling.value;
this.setState({ message: text });
console.log(this.state);//It will show you old value
}
render() {
console.log("check your updated state",this.state)
return (
<div>
<form>
<input type="text" name="customText" />
<button type="button" onClick={this.handleButton}>
Send
</button>
</form>
</div>
);
}
}
I have Candidate list as a parent and Candidate as a child.
On Candidate list there is a Select All input so i bind function to it to set state if candidates are being selected and passing that state to child.
That one part works but those inputs are not mutable by itself just by parent Select All can be changed.
This is what does it looks like:
CandidateList Component(Parent):
class CandidateList extends React.Component {
constructor(props) {
super(props);
this.state = {
candidateList: null,
candidateSelected: false
}
}
onSelectCandidatesClick() {
this.setState({
candidateSelected: !this.state.candidateSelected
});
}
render() {
return (
<div className="CandidateList">
<div className="__selection">
<input className="checkbox" type="checkbox" id="selectAll" onClick={() => this.onSelectCandidatesClick()}/>
<label htmlFor="selectAll"><span id="__label">Select All</span></label>
</div>
</div>
);
}
}
Candidate Component(Child):
class Candidate extends React.Component {
constructor(props) {
super(props);
this.state = {
candidateSelected: false
}
}
componentWillReceiveProps(nextProps) {
this.setState({
candidateSelected: nextProps.selectCandidate
});
}
render() {
return (
<div className="Candidate">
<div className="__select">
<input className="checkbox" type="checkbox" id="candidate" data-candidate-id={this.props.candidateData.id} checked={this.state.candidateSelected} />
<label htmlFor="candidate"></label>
</div>
</div>
);
}
}
Thanks for all suggestions and help.
You need to give the checkbox input in your child component a way to process its change based on an input event.
<input checked={this.state.candidateSelected} onChange={e => {}} />
At the moment you're passing props in to say if it's checked or not so it can be an empty function.
Personally I think this is the wrong approach in any case. It's not an input because the user isn't inputting anything directly to that element.
Maybe you should have a prop called checked and if it is checked then show a check icon rather than an input.
{ this.props.checked ? <div className="checked"></div> : <div className="unchecked"></div>}
or something along those lines.
You can set the checked value of the child based on the props.candidateSelected (if the select all was selected from the parent)
or based on the state (if the checkbox was selected from the child)
You have to add an onclick event handler that will change the state when the checkbox in the child is clicked
class Candidate extends React.Component {
constructor(props) {
super(props);
this.state = {
candidateSelected: false
}
}
onSelectCandidatesClick() {
this.setState({
candidateSelected: !this.state.candidateSelected
});
}
render() {
return (
<div className="Candidate">
<div className="__select">
<input className="checkbox" type="checkbox" id="candidate" data-candidate-id={this.props.candidateData.id}
onClick={() => this.onSelectCandidatesClick()}
checked={this.state.candidateSelected || this.props.candidateSelected} />
<label htmlFor="candidate"></label>
</div>
</div>
);
}
}
This worked for me, but probably not the best option:
checked={isMenuOpen}
onClick={() => toggleDropdown()}
onChange={e => {}}
Have you made custom label for all inputs? 😃 this has gotten me before
I have the following parent component:
class NewPetForm extends React.Component {
state = {
name: '',
age: '',
animal: '',
breed: ''
};
render() {
return (
<StyledNewPetForm >
<Input label="name" />
<Input label="age" />
<Input label="animal"/>
<Input label="breed"/>
<Button type="submit" />
</StyledNewPetForm>
);
}
}
And the following child component:
class Input extends React.Component {
render() {
return (
<StyledWrapper>
<StyledInput
value={this.props.test}
type="text"
/>
</StyledWrapper>
);
}
}
Is it possible to listen to changes in all inputs in children components and update the state accordingly?
What I want to achieve is basically passing handlers to different Inputs and update state dynamically, so there is a possibility to reuse Input component.
changeHandler = (event,stateName) =>{
this.setState({[stateName]:event.target.value]})
}
<Input changed={changeHandler(event,'name')}></Input>
<Input changed={changeHandler(event,'age')}></Input>
<Input changed={changeHandler(event,'breed')}></Input>
// Inside Input
<input onChange={this.props.changed}/>
Thanks for help!
Yes you can do that, you can pass a function to the child element as a property and call it when a change occurs in the child.
Here is an example:
class NewPetForm extends React.Component {
state = {
name: ''
};
onValueChange(key, event) {
this.setState({[key]: event.target.value})
}
render() {
return (
<StyledNewPetForm >
<Input value={this.state.name} onValueChange={this.onValueChange.bind(this, 'name')} />
</StyledNewPetForm>
);
}
}
and the child
class Input extends React.Component {
render() {
return (
<StyledWrapper>
<StyledInput
onChange={this.props.onValueChange}
value={this.props.value}
type="text"
/>
</StyledWrapper>
);
}
}
{[key]: event.target.value} may seem confusing, is just the new syntax that lets you use a string as a property name in an object literal.
The important part is onChange={this.props.onValueChange}, here, I'm calling the parent function NewPetForm.onValueChange when the input's value changes.
I have 2 React parent/child components. The Child Component has a button that adds +1 to the previous state of the Parent Component, and a Form that triggers a handleChange function for the onChange event.
The Problem
From the Form input, I want to trigger a function that sets the State to the previous State, + the input in the Form.
For example, if I write 50 in input and hit submit I want the new state be 100
Here is a codesandbox: https://codesandbox.io/s/30mz2vvyo1
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 50
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState((prevState) => {
return { value: prevState.value + 1 }
});
}
handleSubmit(event) {
event.preventDefault();
}
render() {
return (
<div>
<Child value={this.state.value} handleChange={this.handleChange} handleSubmit={this.handleSubmit} />
</div>
)
}
}
class Child extends React.Component {
render() {
return (
<div>
<button onClick={this.props.handleChange}>Count + 1</button>
<div>{this.props.value}</div>
<form onSubmit={this.props.handleSubmit}>
<label>
Name:
<input type="text" onChange={this.props.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
</div>
)
}
}
The problem you are facing can be mitigated by;
You need to have two different variables in state. value, can hold your value. You also need to hold the current value of the input, let's call it inputNumber.
You need to provide an onClick function to your button. In said function, set your state in the following fashion;
Code:
this.setState({
value: this.state.value + this.state.inputNumber,
})
After doing these things, it should work as expected.
I have updated your codesandbox, you can take a look at it here.
I'm new to React and I'm puzzled on something kind of basic.
I need to append a component to the DOM after the DOM is rendered, on a click event.
My initial attempt is as follows, and it doesn't work. But it's the best thing I've thought to try. (Apologies in advance for mixing jQuery with React.)
ParentComponent = class ParentComponent extends React.Component {
constructor () {
this.addChild = this.addChild.bind(this);
}
addChild (event) {
event.preventDefault();
$("#children-pane").append(<ChildComponent/>);
}
render () {
return (
<div className="card calculator">
<p><a href="#" onClick={this.addChild}>Add Another Child Component</a></p>
<div id="children-pane">
<ChildComponent/>
</div>
</div>
);
}
};
Hopefully it's clear what I need to do, and I hope you can help me attain an appropriate solution.
Don't use jQuery to manipulate the DOM when you're using React. React components should render a representation of what they should look like given a certain state; what DOM that translates to is taken care of by React itself.
What you want to do is store the "state which determines what gets rendered" higher up the chain, and pass it down. If you are rendering n children, that state should be "owned" by whatever contains your component. eg:
class AppComponent extends React.Component {
state = {
numChildren: 0
}
render () {
const children = [];
for (var i = 0; i < this.state.numChildren; i += 1) {
children.push(<ChildComponent key={i} number={i} />);
};
return (
<ParentComponent addChild={this.onAddChild}>
{children}
</ParentComponent>
);
}
onAddChild = () => {
this.setState({
numChildren: this.state.numChildren + 1
});
}
}
const ParentComponent = props => (
<div className="card calculator">
<p><a href="#" onClick={props.addChild}>Add Another Child Component</a></p>
<div id="children-pane">
{props.children}
</div>
</div>
);
const ChildComponent = props => <div>{"I am child " + props.number}</div>;
As #Alex McMillan mentioned, use state to dictate what should be rendered in the dom.
In the example below I have an input field and I want to add a second one when the user clicks the button, the onClick event handler calls handleAddSecondInput( ) which changes inputLinkClicked to true. I am using a ternary operator to check for the truthy state, which renders the second input field
class HealthConditions extends React.Component {
constructor(props) {
super(props);
this.state = {
inputLinkClicked: false
}
}
handleAddSecondInput() {
this.setState({
inputLinkClicked: true
})
}
render() {
return(
<main id="wrapper" className="" data-reset-cookie-tab>
<div id="content" role="main">
<div className="inner-block">
<H1Heading title="Tell us about any disabilities, illnesses or ongoing conditions"/>
<InputField label="Name of condition"
InputType="text"
InputId="id-condition"
InputName="condition"
/>
{
this.state.inputLinkClicked?
<InputField label=""
InputType="text"
InputId="id-condition2"
InputName="condition2"
/>
:
<div></div>
}
<button
type="button"
className="make-button-link"
data-add-button=""
href="#"
onClick={this.handleAddSecondInput}
>
Add a condition
</button>
<FormButton buttonLabel="Next"
handleSubmit={this.handleSubmit}
linkto={
this.state.illnessOrDisability === 'true' ?
"/404"
:
"/add-your-details"
}
/>
<BackLink backLink="/add-your-details" />
</div>
</div>
</main>
);
}
}