React Geosuggest doesn't update state - reactjs

i'm trying to make a weather app, and using external component 'react-geosuggest'.
My problem is, when I update the input (SEE:onChange={this.onInputChange}), that function doesn't take my input, and don't change the state, i.e I get undefined.
What's interesting, i've set initialvalue to be New York, and on submit I get results, without changing input information, so problem lies in updating input information and passing it to function handleOnSubmit.
I've read docs about that component, but couldn't figure it out, it has same values as simple , but something doesn't work.
Thanks!
class SearchBar extends Component {
constructor() {
super()
this.state = {city: 'New York'};
}
onInputChange = (e) => {
this.setState({city: e.target.value});
}
handleOnSubmit = (e) => {
e.preventDefault();
this.props.fetchWeather(this.state.city);
this.setState({city: ''});
}
render () {
return (
<div>
<form onSubmit={this.handleOnSubmit}>
<Geosuggest
initialValue={this.state.city}
onChange={this.onInputChange}
types={['(cities)']}
/>
<button type="submit">Search</button>
</form>
</div>
</div>
);
}
}

Bind the event on the constructor, set the value attribute in the render and remove the setState to empty string that you are doing in the handleOnSubmit event. I am afraid this last one is what it makes not working when you change the input.
class SearchBar extends Component {
constructor(props) {
super(props);
this.state = {city: 'New York'};
this.onInputChange = this.onInputChange.bind(this);
}
onInputChange = (city) => {
this.setState({city: city});
}
handleOnSubmit = (e) => {
e.preventDefault();
this.props.fetchWeather(this.state.city);
}
render () {
return (
<div>
<form onSubmit={this.handleOnSubmit}>
<Geosuggest
initialValue={this.state.city}
value={this.state.city}
onChange={this.onInputChange}
types={['(cities)']}
/>
<button type="submit">Search</button>
</form>
</div>
</div>
);
}
}

Use e.currentTarget.value instead. See this question for more on this.
Also try this:
onChange={this.onInputChange.bind(this)}
If you'd like to be more concise you can write it this way:
<Geosuggest
initialValue={this.state.city}
onChange={(e)=> this.setState({city: e.currentTarget.value})}
types={['(cities)']}
/>

You can also get the value of selected address using onSuggestSelect:
<Geosuggest
ref={el=>this._geoSuggest=el}
placeholder="Search for address"
onSuggestSelect={this.onSuggestSelect}
country='us'
/>
Then access the components like so:
onSuggestSelect(suggest) {
if (!suggest.gmaps) {
return;
}
const components = suggest.gmaps.address_components;
const address = {
street: this.findAddressComponent(components, 'street_number', 'short_name') + ' ' + this.findAddressComponent(components, 'route', 'short_name'),
city: this.findAddressComponent(components, 'locality', 'short_name'),
state: this.findAddressComponent(components, 'administrative_area_level_1', 'short_name'),
zip: this.findAddressComponent(components, 'postal_code', 'short_name'),
}
this.setState({
address
});
}
findAddressComponent(components, type, version) {
const address = components.find(function(component) {
return (component.types.indexOf(type) !== -1);
});
return address[version];
}

Related

Why my component is not changing state when I trigger the function

I am in new in React and I am trying to change the state when the user type some values in the inout. For a reason the component does not seem to work. Could anyone explain me why I am not succeeding in implementing this function? Thanks, Valentino
import React, { Component } from 'react';
class Stake extends Component {
state = ({
userStake: null
});
handleUserStake = (e) => {
e.preventDefault();
let newStake = e.target.value;
this.setState({
userStake: [...userStake, newStake]
})
}
render() {
return (
<div>
<form onSubmit={this.handleUserStake}>
<input
style={{
marginLeft: "40px",
width: "50px"
}}
type="text"
name="stake"
required
/>
</form>
</div >
);
}
}
export default Stake;
to accomplish a controlled input you should add the property value pointing to the respective state, and onChange that will handle its state update. for your onSubmit you use another handler specific to it. you should also fix your handleChange to update its value correctly:
class Stake extends Component {
state = ({
userStake: [],
input: ''
});
handleUserStake = (e) => {
let input = e.target.value;
this.setState({ input });
}
handleSubmit = () => {
e.preventDefault();
let newStake = this.state.input;
this.setState({
userStake: [...this.state.userStake, newStake],
input: '',
})
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input
style={{
marginLeft: "40px",
width: "50px"
}}
type="text"
name="stake"
value={this.state.input}
onChange={this.handleUserStake}
required
/>
</form>
</div>
);
}
}
export default Stake;
You need to first get userStake from the state
handleUserStake = (e) => {
e.preventDefault();
let newStake = e.target.value;
// you need to first get it from the state before updating
const { userStake } = this.state;
this.setState({
userStake: [...userStake, newStake]
})
}
Firstly you should create constructor for your component with state and binded `handleUserStakez method.
Because you are waiting for the submit event from form you would use ref for the input:
this.inputRef = React.createRef();
ref={this.inputRef}
Full code here:
class Stake extends Component {
constructor(props) {
super(props);
this.state = {
userStake: ""
};
this.handleUserStake = this.handleUserStake.bind(this);
this.inputRef = React.createRef();
}
handleUserStake = (e) => {
e.preventDefault();
let newStake = this.inputRef.current.value;
console.log("newStake", newStake);
this.setState({
...this.state,
userStake: newStake
});
};
render() {
return (
<div>
STATE: {this.state.userStake}
<form onSubmit={this.handleUserStake}>
<input
style={{
marginLeft: "40px",
width: "50px"
}}
type="text"
name="stake"
required
ref={this.inputRef}
/>
</form>
</div>
);
}
}
this should work ...
state = {
userStake: [],
};
handleUserStake = (e) => {
e.preventDefault();
let newStake = e.target.stake.value;
this.setState((state) => ({
userStake: [...state.userStake, newStake],
}));
};
what was problem? :-
getting value you need to add NAME from target which is form to get field value. like ...
let newStake = e.target.stake.value;
getting current state value, your were just getting userStake out of nowhere so it was giving undefined error.
this.setState((state) => ({ userStake: [...state.userStake, newStake], }));
make default value to [] for userStake in state obj.
userStake: []
I think I found the solution. The problem was that I had to write [...this.state.userStake, newStake]

Can't type in React input text field in Todo app

I am making a todo app in React.js but stuck in this part. I can not type in input field. Please help me.
import React, { Component } from 'react';
export default class AddItem extends Component {
state =
{
title: "",
done: false
}
changeTitle = (e) =>{
e.preventDefault();
this.setState = ({
title: e.target.value
});
}
addNewItem = (item) => {
item.preventDefault();
let newTask = {
title: this.state.title,
done: false
};
this.props.additem(newTask);
this.setState = ({
title: ""
});
}
render() {
return (
<div>
<form>
<input
type="text"
placeholder="add task name."
value={this.state.title}
onChange= {this.changeTitle}
/>
<button type = "button" onClick= {this.addNewItem} >submit</button>
</form>
</div>
)
}
}
this.setState is a function that is called with an object containing changes in state. The code you are using here is an assignment not a function call:
this.setState = ({
title: e.target.value // Wrong
});
Instead, call the setState function with the changes/updates in state. the changes are shallow merged and only title is updated here.
this.setState({title:e.target.value});
You will have a similar problem inside addNewItem.

I have two set of input fields for two different forms. How can I use them dynamically for any form?

I am new to react. I am trying to build dynamic form component of a set of input fields using react which can be reused in any single form as a set of inputs. How can I access those input data dynamically?
For example, the form for the set of reusable inputs:
export default class dynamicForm extends Component {
handleSubmit = event => {
// get dynamic input data. But how?
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<CustomInputs1/>
<CustomInputs2/>
<input type="submit" value="confirm"/>
</form>
)}
Input set no. 1 as custom input:
export default class CustomInputs1 extends Component {
constructor(props) {
super(props);
this.state = {
input1: "",
input2: ""
};
this.inputInput1Ref = React.createRef();
this.inputInput2Ref = React.createRef();
}
handleInputChange = event => {
event.preventDefault();
this.setState({
[event.target.name]: event.target.value
});
};
render() {
const { input1 } = this.state;
const { input2 } = this.state;
return (
<div>
<input type="text" name="input1" value={input1} onChange={this.handleInputChange}/>
<input type="text" name="input2" value={input2} onChange={this.handleInputChange}/>
</div>
)
}
}
Input set no. 2 as custom input:
export default class CustomInputs2 extends Component {
constructor(props) {
super(props);
this.state = {
input3: "",
input4: ""
};
this.inputInput3Ref = React.createRef();
this.inputInput4Ref = React.createRef();
}
handleInputChange = event => {
event.preventDefault();
this.setState({
[event.target.name]: event.target.value
});
};
render() {
const { input3 } = this.state;
const { input4 } = this.state;
return (
<div>
<input type="text" name="input3" value={input3} onChange={this.handleInputChange}/>
<input type="text" name="input4" value={input4} onChange={this.handleInputChange}/>
</div>
)
}
}
There can be more reusable input groups like this.I just added two group as example.
A few things: You are not using the refs you are creating in the CustomInputs, so those lines can go. Also, you can destructure the state in one line, like so: const {input1, input2} = this.state.
Now for your problem: You can either create refs to the CustomInputs in your dynamicForm and simply access the state of your custom inputs when the form is submitted or pass a function as props to each CustomInputs which writes the input to the state of dynamicForm. First option is probably easier ;)

onChange or onKeyUp event takes previous value in ReactJS

I've created form in ReactJS. I am fetching value in common method in {key : value} paired. but I am getting previous value in method.
constructor(props) {
super(props);
{
this.state = { name: "", age: 0 };
}
}
inputChange = (key, value) => {
this.setState({ [key] : value });
console.log(this.state);
}
render() {
return (
<form>
<div>
Name : <input type="text" name="name" onKeyUp={(e) => this.inputChange('name', e.target.value)}></input>
</div>
<div>
Age : <input type="text" name="age" onKeyUp={(e) => this.inputChange('age', e.target.value)}></input>
</div>
</form>
)
}
I've attached the screenshot for better understanding.
setState enqueues a change to the state, but it doesn't happen immediately. If you need to do something after the state has changed, you can pass a second callback argument to setState:
inputChange = (key, value) => {
this.setState({ [key] : value }, () => {
console.log(this.state);
});
}
This will do exactly what you need.
class App extends Component {
constructor(props){
super(props)
this.state = {
name: '',
age: ''
}
}
handleInput(option, event){
if (option === 'name') {
this.setState({
name: event.target.value
}, () => {
console.log("Name: ", this.state.name)
});
}
else if (option === 'age'){
this.setState({
age: event.target.value
}, () => {
console.log("Age: ", this.state.age)
});
}
}
render() {
return (
<div>
<div>
<header>Name: </header>
<input type="text" onChange={this.handleInput.bind(this, 'name')}/>
<header>Age: </header>
<input type="text" onChange={this.handleInput.bind(this, 'age')}/>
</div>
</div>
);
}
}
update 2022
with functional components using useState hook this does not work longer, you have to use it in useEffect to update after rendering.
State updates from the useState() and useReducer() Hooks don't support the second callback argument. To execute a side effect after rendering, declare it in the component body with useEffect().

Can't render {this.state.city}

I'm creating a weather app in React using OpenWeatherMap API. There are input form and a button, and I'm expecting to see city name when I click the botton. I received data from the API when I do so, but can't render it on a screen while I can log it in a console.
For this, I'm using three separated files. App.js, Form.js for submitting terms, and weather.js for API configuration.
I'm guessing that I need to map the received data but not yet successful.
class App extends React.Component {
state = {
city: null,
}
getWeather = async city => {
const response = await weather.get('/forecast', {
params: {
q: city
}
});
this.setState({
city: response.name,
})
console.log(city); <--- This works
}
render() {
return (
<div>
<Form loadWeather={this.getWeather} />
<p>{this.state.city}</p> <--- This doesn't work
</div>
);
}
}
class Form extends React.Component {
state = { term: '' };
onFormSubmit = (event) => {
event.preventDefault();
this.props.loadWeather(this.state.term);
this.refs.textInput.value = '';
}
render() {
return (
<div>
<form onSubmit={this.onFormSubmit}>
<input
ref="textInput"
type="text"
value={this.state.term}
onChange={event => this.setState({term: event.target.value})}
/>
<button>Get Weather</button>
</form>
</div>
);
}
}
export default Form;
I'm going to pass the {this.state.name} as a prop to a child component, but so far the received data doesn't even appear on that component ifself.
"this.setState" is a function, should be called like this.
this.setState({
city: response.name,
})
You're setting state's city to response.name. You tagged the question as axios, so I'm assuming you're using axios for the ajax. If so, you'll want to get the data from the response back from response.data.name, not response.name.
If you want to get the city details from the initial render try calling/invoking the getWeather() method in componentDidMount() life cycle. The way you are using setState() is also wrong. it should be something like this as mentioned below. The same lifecyle could be used to get the data even though you have used a separate file to invoke the method
class App extends React.Component {
state = {
city: null
};
componentDidMount() {
this.getWeather("city");
}
getWeather = async city => {
const response = await weather.get("/forecast", {
params: {
q: city
}
});
this.setState({
city: response.name
});
};
render() {
return (
<div>
<p>{this.state.city}</p>
</div>
);
}
}

Resources