This question already has answers here:
How to pass data from child component to its parent in ReactJS?
(18 answers)
Pass props to parent component in React.js
(7 answers)
Closed 5 years ago.
In react js I have 2 components.
In Component1.jsx :
import Info from Component2.jsx;
...
...
var dataInfo = "some info.. ";
<Info dataInfo={dataInfo} />
...
...
Here in the above code, we'r transfering the data in the form props from component1.jsx to component2.jsx
In the same fashion can we transfer back to data to component2.jsx to component1.jsx ?
Please help me out here. I'm trying to find the answer in google but couldn't get properly.
Yes you can transfer back to parent component,
i will give you an example to show clearly how you can do that
suppose you have a parent it's called component1 and it have a form imported as a child in it called component2
as the follow:
import React, { Component } from 'react';
export default class Component2 extends Component{
constructor() {
super();
this.state = {
UserName: '',
email: ''
};
this.onSubmit = this.onSubmit.bind(this)
}
onSubmit(e){
e.preventDefault();
var field = {
UserName: this.state.UserName,
email : this.state.email,
password: this.state.password,
}
**this.props.onUpdate(field);**
}
onChange(e){
this.setState({
[e.target.name]: e.target.value
});
}
render() {
var UserNameError = **this.props.UserNameError**;
var emailError = **this.props.emailError**;
return(
<div className="col-md-6 col-sm-6">
<div className="title">Create Account</div>
<Form onSubmit={this.onSubmit}>
<div className="form-group">
<label>user Name</label>
<input onChange={this.onChange.bind(this)} value={this.state.UserName} name='UserName'/>
<span className="error is-visible">{UserNameError}</span>
</div>
<div className="form-group">
<label>Email</label>
<input onChange={this.onChange.bind(this)} value={this.state.email} name='email' />
<span className="error is-visible">{emailError}</span>
</div>
<Button className='btn submit'>Register</Button>
</Form>
</div>
)
}}
import React, { Component } from 'react';
import Component2 from './Component2'
export default class Component1 extends Component{
constructor() {
super();
this.state = {
UserName: '',
email: '',
UserNameError:' UserNameError ',
emailError:' emailError '
};
}
onUpdate(val) {
this.setState({
email: val.email,
UserName: val.UserName,
});
console.log(' onSubmit ** email' + val.email + " UserName " + val.UserName )
};
render() {
return(
<div className="col-md-6 col-sm-6">
<Component2 **UserNameError={this.state.UserNameError}** **emailError={this.state.emailError}** **onUpdate={this.onUpdate.bind(this)}** />
</div>
)
}
}
I put the stars around sentence to notice how I transfer data errors from parent Component1 to component2
and how I send Form data by onUpdate function from child Component2 to Component1
Related
Trying to learn React/RoR and in this simple test app I have a 'searchapp' react app. That sets a default for the language radioboxes.
import React from 'react'
import ReactDOM from 'react-dom'
import axios from "axios";
import SearchForm from "./searchForm";
class SearchApp extends React.Component {
constructor(props) {
super(props);
this.state = {
searchStrings: [],
subjectName: "",
language: 'English',
region: ""
};
this.getSearchStrings = this.getSearchStrings.bind(this);
}
componentDidMount() {
this.getSearchStrings();
}
handleClickLang = changeEvent => {
this.setState({
language: changeEvent.target.value
});
};
getSearchStrings() {
axios
.get("/api/v1/search_strings")
.then(response => {
const searchStrings = response.data;
this.setState({searchStrings});
})
.catch(error => {
console.log(error);
});
}
render() {
return (
<>
<SearchForm />
</>
);
}
}
and then in the searchForm component I use that state to set and switch between two radio buttons.
class SearchForm extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.subjRef = React.createRef();
//this.handleClickLang = this.handleClickLang.bind(this);
}
componentDidMount() {
this.setState({
language: 'English'
});
}
handleSubmit(e) {
e.preventDefault();
window.alert("sometext");
}
render() {
return (
<form onSubmit={this.handleSubmit} className="my-3">
<div className="form-row">
<div className="form-group col-md-8">
<p>Choose language</p>
</div>
<div className="form-row">
<div className="form-check">
<label>
<input
type="radio"
name="react-tips"
value="English"
checked={this.state.language === 'English'}
onChange={this.handleClickLang}
className="form-check-input"
/>
English
</label>
</div>
<div className="form-check">
<label>
<input
type="radio"
name="react-tips"
value="Russian"
checked={this.state.language === 'Russian'}
onChange={this.handleClickLang}
className="form-check-input"
/>
Russian
</label>
</div>
However when I run this I get the following error:
Uncaught TypeError: Cannot read properties of null (reading 'language')
I thought this was erroring because it cannot find a default language, however I initialised language to 'English' in the constructor for the SearchApp.
Is this me not understanding React state enough? Any help much appreciated.
In the componentDidMount i feel the language is getting to null, you can try the following:
Trying to learn React/RoR and in this simple test app I have a 'searchapp' react app. That sets a default for the language radioboxes.
import React from 'react'
import ReactDOM from 'react-dom'
import axios from "axios";
import SearchForm from "./searchForm";
class SearchApp extends React.Component {
constructor(props) {
super(props);
this.state = {
searchStrings: [],
subjectName: "",
language: 'English',
region: ""
};
this.getSearchStrings = this.getSearchStrings.bind(this);
}
componentDidMount() {
this.getSearchStrings();
}
handleClickLang = changeEvent => {
this.setState({
language: changeEvent.target.value
});
};
getSearchStrings() {
axios
.get("/api/v1/search_strings")
.then(response => {
const searchStrings = response.data;
this.setState({searchStrings,language: 'English'});
})
.catch(error => {
console.log(error);
});
}
render() {
return (
<>
<SearchForm language={this.state.language}/>
</>
);
}
}
<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>
And in the SearchForm component pass the language via props
class SearchForm extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.subjRef = React.createRef();
//this.handleClickLang = this.handleClickLang.bind(this);
}
handleSubmit(e) {
e.preventDefault();
window.alert("sometext");
}
render() {
return (
<form onSubmit={this.handleSubmit} className="my-3">
<div className="form-row">
<div className="form-group col-md-8">
<p>Choose language</p>
</div>
<div className="form-row">
<div className="form-check">
<label>
<input
type="radio"
name="react-tips"
value="English"
checked={this.props.language === 'English'}
onChange={this.handleClickLang}
className="form-check-input"
/>
English
</label>
</div>
<div className="form-check">
<label>
<input
type="radio"
name="react-tips"
value="Russian"
checked={this.props.language === 'Russian'}
onChange={this.handleClickLang}
className="form-check-input"
/>
Russian
</label>
</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>
But there are several more things:
Why are you also having language state in searchForm, you can have it in Search component and pass it via props
Yo are not initializing any state in the constructor in your SearchForm component
Adding a followup here for future reference.
As suggested by #Thor84no the problem was not adding state = {} to intialise state in the sub component.
I wish to share this code just in case someone might need to solve such a problem of filtering unwanted characters when doing forms in react. For extras, my code shows how to pass props to components inside Route. For simplicity, I have focused on only these two inputs and omitted other stuff such as the submit button and css data for styling those classNames and ids.
import React, { Component } from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";
import SignupForm from "./components/SignupForm";
class App extends Component {
constructor() {
super();
this.state = {
firstName: "",
lastName: "",
};
//Binding this to the functions used as they wouldn't just work if not bound
this.changeFirstName = this.changeFirstName.bind(this);
this.changeLastName = this.changeLastName.bind(this);
this.lettersOnly = this.lettersOnly.bind(this);
}
changeFirstName(e) {
this.setState({ firstName: e.target.value });
}
changeLastName(e) {
this.setState({ lastName: e.target.value });
}
// Error handler functions
lettersOnly(nameInput) {//Replacing all characters except a-z by nothing with this function
let regex = /[^a-z]/gi;
nameInput.target.value = nameInput.target.value.replace(regex, "");
}
render() {
return (
<Router>
<div className="App">
<Route
exact
path="/"
comp={SignupForm}
render={() => (
<SignupForm
//SignupForm submit props
changeFirstNameHandler={this.changeFirstName}
firstNameValue={this.state.firstName}
changeLastNameHandler={this.changeLastName}
lastNameValue={this.state.lastName}
// Error handlers
nameCharacterFilter={this.lettersOnly}
/>
)}
/>
)}
/>
</div>
</Router>
);
}
}
export default App;
Below is the signup form, which is the child component in this aspect, and also a function component as opposed to its parent component:
import React from "react";
export default function SignupForm(props) {
return (
<div className="container" id="signupForm">
<h1>Signup Form</h1>
<div className="form-div">
<form>
<input
type="text"
placeholder="First Name"
onChange={props.changeFirstNameHandler}
value={props.firstNameValue}
onKeyUp={props.nameCharacterFilter}
className="form-control formgroup"
/>
<input
type="text"
placeholder="Last Name"
onChange={props.changeLastNameHandler}
value={props.lastNameValue}
onKeyUp={props.nameCharacterFilter}
className="form-control formgroup"
/>
</form>
</div>
</div>
);
}
NB: Welcome to improve this code, if you feel the need!
I think you can improve you're code with this changes:
Use the regex directly in the onChange event
Use only one method to update the values
Here is an example of what I mean: https://codesandbox.io/s/react-playground-forked-vreku?fontsize=14&hidenavigation=1&theme=dark
Regards!
Okay here is an improved code and much more cleaner. However, I have just omitted the React Router part to focus on functionality of the state and functions in this case.
I also want the user to see when they type an unwanted character that it actually typed but then just deleted on key up so I have created an independent function justLettersAndHyphen(nameField) from changeValue(event) that is triggered by onKeyUp.
import React from "react";
import SignupForm from "./SignupForm";
class App extends React.Component {
constructor() {
super();
this.state = {
firstName: "",
lastName: ""
};
this.changeValue = this.changeValue.bind(this);
this.justLettersAndHyphen = this.justLettersAndHyphen.bind(this);
}
changeValue(event) {
this.setState({
[event.target.name]: event.target.value,
});
}
// Error handler functions
justLettersAndHyphen(nameField) {
let regex = /[^a-z-]/gi;
nameField.target.value = nameField.target.value.replace(regex, "");
}
render() {
return (
<SignupForm
firstNameValue={this.state.firstName}
lastNameValue={this.state.lastName}
changeValueHandler={this.changeValue}
nameCharacterFilter={this.justLettersAndHyphen}
/>
);
}
}
export default App;
Child component edited with name property added.
import React from "react";
export default function SignupForm(props) {
return (
<div className="container" id="signupForm">
<h1>Signup Form</h1>
<div className="form-div">
<form>
<input
name="firstName"
type="text"
placeholder="First Name"
onChange={props.changeValueHandler}
onKeyUp={props.nameCharacterFilter}
value={props.firstNameValue}
/>
<input
name="lastName"
type="text"
placeholder="Last Name"
onChange={props.changeValueHandler}
onKeyUp={props.nameCharacterFilter}
value={props.lastNameValue}
/>
</form>
</div>
</div>
);
}
This question already has answers here:
How to pass data from child component to its parent in ReactJS?
(18 answers)
Closed 5 years ago.
I can't figure out why I can't get data at my parent component.
I have parent component - AddButton , and child component - AddTasktBox. I want to pass title from child to parent.
Probably bad is in AddButton component - because browser is give me error like this:
"TypeError: _this2.props.sendData is not a function"
Just look at submit - input "onClick={this.handleClick}"(bottom) - there's start my code to passing
import React from 'react';
import ReactDOM from 'react-dom';
class AddButton extends React.Component{
constructor(props){
super(props);
this.state = {
isHidden: false,
title: '',
};
}
sendData = (data) => {
console.log(data);
this.setState({
title: data
})
};
toggleHidden = () => {
this.setState({
isHidden: !this.state.isHidden
})
}
render(){
return(
<div>
<div
onClick={this.toggleHidden.bind(this)}
className="addtask__btn">
+
</div>
{this.state.isHidden && <AddTaskBox handleClose={this.toggleHidden.bind(this)} handleClick={this.sendData.bind(this)}/>}
</div>
);
}
}
class AddTaskBox extends React.Component{
constructor(props){
super(props);
this.state = {
title: '',
description: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleClick = this.handleClick.bind(this);
}
handleChange = (e) => {
this.setState({
[e.target.name]: e.target.value
})
}
handleClick = () => {
this.props.sendData(this.state.title);
}
render(){
return(
<div className="addtask__box" >
<div className="addtask__close" onClick={this.props.handleClose}>X</div>
<form >
<input
type="text"
name="title"
placeholder="Nazwa Czynności"
value={this.state.title}
onChange={this.handleChange}/>
<textarea
name="description"
value={this.state.description}
onChange={this.handleChange}
placeholder="Opis czynności">
</textarea>
<input
className="btn"
type="submit"
value="submit"
onClick={this.handleClick}/>
</form>
</div>
);
}
}
export {AddButton, AddTaskBox};
You aren't passing sendData() as a prop to AddTaskBox
<AddTaskBox sendData={this.sendData} />
I'm working whith one representational component (ProjectFormUpdate) and his container (ProjectFormUpdateContainer). From the container, i send an document object Project and a flag isLoading. But in a Constructor() of ProjectFormUpdate, the flag is false... the state is never seted.
The representational componente
import React, { Component} from 'react';
import ReactDOM from 'react-dom';
import { Projects } from '/imports/api/projects.js';
import PropTypes from 'prop-types'; // ES6
import { withTracker } from 'meteor/react-meteor-data';
export default class ProjectFormUpdate extends Component {
handleUpdate(event) {
event.preventDefault();
console.log("se modificó el estadoooo")
this.setState({
codigo: ReactDOM.findDOMNode(this.refs.codigoInput).value.trim(),
nombre: ReactDOM.findDOMNode(this.refs.nombreInput).value.trim()
});
}
handleSubmit(event){
this.setState({
codigo: ReactDOM.findDOMNode(this.refs.codigoInput).value.trim(),
nombre: ReactDOM.findDOMNode(this.refs.nombreInput).value.trim()
});
}
constructor(props) {
super(props);
if (!props.isLoading){
this.state = {
codigo: props.oneProject.codigo,
nombre: props.oneProject.nombre}
}
else{
this.state = {
codigo: 'dd',
nombre: 'ff'}
}
}
render() {
const { oneProject, isLoading } = this.props;
if (!isLoading){
this.setState = {
codigo: this.props.oneProject.codigo,
nombre: this.props.oneProject.nombre}
return (
<div className="col-xs-11">
<div className="box box-solid">
<form className="form" onSubmit={this.handleSubmit.bind(this)} >
<div className="box-body">
<div className="row">
<div className="col-xs-2">
<input
className = "form-control input-sm"
type="text"
ref="codigoInput"
placeholder="Código del Proyecto"
value = {this.state.codigo}//this.state.codigo}
onChange = {this.handleUpdate.bind(this)}
/>
</div>
<div className="col-xs-6">
<input
className = "form-control input-sm"
type="text"
ref="nombreInput"
placeholder="Título"
value = {this.state.nombre }
onChange = {this.handleUpdate.bind(this)}
/>
</div>
</div>
</div>
<div className="box-footer">
<button type="submit" className="btn btn-sm btn-primary btn-flat">Guardar</button>
</div>
</form>
</div>
</div>
);
}
else {return (<div></div>);}
}}
ProjectFormUpdate.propTypes = {
// This component gets the task to display through a React prop.
// We can use propTypes to indicate it is required
oneProject: React.PropTypes.object,
isLoading: React.PropTypes.bool,
};
The Container
import { Meteor } from 'meteor/meteor';
import { withTracker } from 'meteor/react-meteor-data';
import { Projects } from '/imports/api/projects.js';
import ProjectFormUpdate from './ProjectFormUpdate.jsx';
export default ProjectFormUpdateContainer = withTracker(({ key1 }) => {
const sub = Meteor.subscribe('projects');
var oneProject = Projects.findOne(key1);
var isLoading = !sub.ready();
return {
oneProject,
isLoading,
};
})(ProjectFormUpdate);
So... if i can't set the state, i can't set the form's values in a controled way. Any suggestion?
In order to set your components state outside of the constructor() function: you must call this.setState(). this.setState() will set it's first argument as the new state and subsequently call your component's render function.
Your if (!isLoading) statement is very dangerous. Assuming !isLoading == true: your render function will infinitely fire this.setState(), thereby locking your browser.
Your constructor function appears correct, as is. I would allow it to set the initial application state and handle the rest from within the render() function. Alternatively, you could set your initial state within the componentWillMount() or componentDidMount() functions found here.
Within your render() function, I would omit the if (!isLoading) part and instead try returning a loading component if (isLoading == true).
You can also apply the following logic directly to your <input/> elements to set your component's state with finesse:
<input value={this.state.key} onChange={(event) => this.setState({key: event.target.value})}/>
I've revised your ProjectFormUpdate component as follows:
import React, { Component} from 'react';
import ReactDOM from 'react-dom';
import { Projects } from '/imports/api/projects.js';
import PropTypes from 'prop-types'; // ES6
import { withTracker } from 'meteor/react-meteor-data';
export default class ProjectFormUpdate extends Component {
handleSubmit(event){
event.preventDefault()
console.log()
}
constructor(props) {
super(props);
if (!props.isLoading) {
this.state = {
codigo: props.oneProject.codigo,
nombre: props.oneProject.nombre
}
}
else {
this.state = {
codigo: '',
nombre: ''
}
}
}
render() {
const { oneProject, isLoading } = this.props;
if (isLoading) {
return (
<div>isLoading == true</div>
)
}
return (
<div className="col-xs-11">
<div className="box box-solid">
<form className="form" onSubmit={this.handleSubmit.bind(this)} >
<div className="box-body">
<div className="row">
{/* Codigo. */}
<div className="col-xs-2">
<input className = "form-control input-sm" type="text" ref="codigoInput" placeholder="Código del Proyecto" value={this.state.codigo} onChange={(event) => this.setState({codigo: event.target.value})} />
</div>
{/* Nombre. */}
<div className="col-xs-6">
<input className = "form-control input-sm" type="text" ref="nombreInput" placeholder="Título" value={this.state.nombre} onChange={(event) => this.setState({nombre: event.target.value}))} />
</div>
</div>
</div>
<div className="box-footer">
<button type="submit" className="btn btn-sm btn-primary btn-flat">Guardar</button>
</div>
</form>
</div>
</div>
)
}
ProjectFormUpdate.propTypes = {
oneProject: React.PropTypes.object,
isLoading: React.PropTypes.bool,
};
I am trying to write a program that takes names of 2 cities as an input, and then gets the temperature in the 2 cities using the openweathermap API. However, I am unable to call the API. I tried following some tutorials but it is a bit confusing. I would be glad if someone could help me with connecting to the API, and getting the temperature for the cities, and print them on screen.
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import _ from 'lodash';
import Request from 'superagent';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {input1: '', input2: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({[event.target.name]: event.target.value});
}
componentDidMount(){
var city1 = this.state.input1;
var url= 'http://api.openweathermap.org/data/2.5/weather?q={city1}&units=metric&APPID=83c6ba4dd07d83514536821a8a51d6d5';
Request.get(url).then((response) => {
this.setState({
report: response.body.Search
});
});
}
handleSubmit(event) {
var temperature = _.map(this.state.report, (city) => {
return (<p>{city.main.temp}</p>);
});
event.preventDefault();
}
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
</header>
<div className="container">
<h1>Where Should I go?</h1>
<form onSubmit={this.handleSubmit}>
<div className="cityboxdiv">
<input name="input1" type="text" id="tb1" className="citybox" onChange={this.handleChange} placeholder="Enter City1" autoFocus />
<input name="input2" type="text" id="tb2" className="citybox" onChange={this.handleChange} placeholder="Enter City2"/>
</div>
<div className="btn-div">
<button type="submit" className="sub-btn">Tell Me!</button>
</div>
</form>
</div>
</div>
);
}
}
export default App;
Two things I would like to know are how to pass the city names into the url so that we can get the data. And once we get the data, how can I display only the temperature values of the cities.
I see you make several mistakes.
Request to API and put data to state you should make inside handleSubmit (or better use actions).
In render method you should output your state data as state.report.map(item => (<div>{city.main.temp}</div>))
Generate url you can make using template string (https://learn.microsoft.com/en-us/scripting/javascript/advanced/template-strings-javascript)