Getting User Input in React Js in Fetch statement - html

I have created a code for fetching weather data from api. Here in the link, I have given a Place q=chennai. I need to change this place field depends on user. that is I want to get this location from user. How can I do this using react
import React,{useState,useEffect} from 'react';
const Home1=()=>{
const[info,setInfo]=useState({
name:"loading !!",
temp:"loading",
humidity:"loading",
desc:"loading",
})
useEffect(()=>
{
getWeather()
})
const getWeather=()=>
{
fetch('http://api.openweathermap.org/data/2.5/weather?q=chennai&appid=bd2d67a73f2dc8279......')
.then(data=>data.json())
.then(results=>{
//console.log(results)
setInfo({
name:results.name,
temp:results.main.temp,
humidity:results.main.humidity,
desc:results.weather[0].description,
})
})
}
return(
<div>
<h1>Weather API</h1>
<div>
Place:{info.name} <br/>
Temperature:{info.temp} <br/>
Humidity:{info.humidity} <br/>
Description: {info.desc}
</div>
</div>
)
}
export default Home1;```
Can somebody help me to do this ?

You have to take the input from the user and write a onChange event handler. Once you get the input you can call the getWeather function and pass the value you got from the user to the function. Here is the solution -
import React,{useState,useEffect} from 'react';
const Home1=()=>{
const[info,setInfo]=useState({
name:"loading !!",
temp:"loading",
humidity:"loading",
desc:"loading",
})
const [inputValue , setInputValue] = useState("");
useEffect(()=>
{
getWeather("chennai")
})
const getWeather=(value)=>
{
fetch(`http://api.openweathermap.org/data/2.5/weather?q=${value}&appid=bd2d67a73f2dc8279......`)
.then(data=>data.json())
.then(results=>{
//console.log(results)
setInfo({
name:results.name,
temp:results.main.temp,
humidity:results.main.humidity,
desc:results.weather[0].description,
})
})
}
const handleInputChange = event =>{
let inputValue = event.target.value;
console.log(event.target.value); // You will the value here. you can simply pass the value to the function
setInputValue(inputValue);
getWeather(inputValue);
}
return(
<div>
<h1>Weather API</h1>
<div>
Place:{info.name} <br/>
Temperature:{info.temp} <br/>
Humidity:{info.humidity} <br/>
Description: {info.desc}
<input type="text" value={inputValue} onChange={handleInputChange} />
</div>
</div>
)
}
export default Home1;

You can use a controlled input to gives to the user the possibility to choose the location.
const [city, setCity] = useState("chennai") // Initialize city default value
...
useEffect(()=> { getWeather(city) }, [city])
// Add the city as dependency to your effect and pass the value to getWeather method
...
fetch(`http://api.openweathermap.org/data/2.5/weather?q=${city}&appid=bd2d67a73f2dc8279......`)
// Make the url to fetch a template string so you can inject the city state in it
...
<input type="text" value={city} onChange={(e) => setCity(e.target.value)} />
// Add the input with onChange method
If you want to know more about controlled input in React this is the doc related page https://reactjs.org/docs/forms.html

Related

Unable to display mysql query result in the browser with axios in react

I was trying to retrieve data from database and display it in the browser on button click but I always receive this error (Uncaught Error: Objects are not valid as a React child (found: object with keys {studentId, firstName, surName, otherName, state, localGovt, phoneNumber, imgUrl}). If you meant to render a collection of children, use an array instead.). I was able to see the data when I use console.log, but failed in the DOM.
Here is the code on the frontend.
import Axios from "axios";
import { useState, useEffect } from "react";
const SearchUsers = () => {
const [input, setInput] = useState("");
const [studentResult, setStudentResult] = useState([]);
const handleSearch = async () => {
//const sanitizedInput = input.trim().toUpperCase();
try {
const { data } = await Axios.get("http://localhost:3500/students");
const { result } = data; // this is an array
console.log(result)//this works. it displays array of objects with the data from the db
setStudentResult(result);
} catch (error) {
console.log(error);
}
};
return (
<>
<div className="flex flex-col w-2/4 mx-auto my-20">
<div>
<input
className="input w-full"
type="text"
name="search"
placeholder="Search a Student"
onChange={(e) => setInput(e.target.value)}
/>
</div>
<div className="flex justify-center mt-5">
<button
type="button"
className="btn shadow shadow-gray-500"
onClick={handleSearch}
>
Get All Students
</button>
</div>
<div>
{studentResult.map((item, index) => {
return (
<div key={index}>
<p>{item.firstName}</p>
</div>
);
})}
</div>
</div>
</>
);
};
export default SearchUsers;
Here is code on the backend. It's a controller function that retrieves all data from the database
const getAllStudents = (req, res) => {
const selectAll = "SELECT * FROM students";
studentDB.query(selectAll, (err, result) => {
res.sendStatus(200).json({ result: result });
});
};
What's going wrong?
The problem arises when you:
Initialize useState with an empty array, and
Didn't define its type;
Then, the type of array will implicitly be assigned to never[], meaning that array should always be empty.
Therefore, mutating that array, including via setStudentResult(newArray), will always fail.
How to fix it
To solve this, we need to explicitly define the type of the empty array. For example:
const [studentResult, setStudentResult] = useState<{name: string, grade:number}[]>([]);
You should change the type declaration above based on the data structure of result.
Hope it helps. Cheers.

Storing Multiple Form submission containing multiple inputs with React

I am building a signup form containing multiple inputs(username, date of birth, etc). After a form is submitted it is stored somewhere containing a list of previous submitted forms.
Link to working sample: https://codesandbox.io/s/react-form-multiple-storage-xcmt3i?file=/src/App.js
Codebase for working sample:
import React, { useState } from "react";
const App = () => {
const [values, setValues] = useState({
userName: "",
dateOfBirth: ""
});
const [submissions, setSubmission] = useState([
{
userName: "april123",
dateOfBirth: "2000-01-01"
}
]);
const addSubmission = (values) => {
const newSubmissions = [...submissions, { values }];
setSubmission(newSubmissions);
console.log(submissions);
};
const handleChange = (event) => {
const value = event.target.value;
setValues({
...values,
[event.target.name]: value
});
};
const handleSubmit = (event) => {
event.preventDefault();
addSubmission(values);
};
return (
<>
<form onSubmit={handleSubmit}>
<h1> Signup Form </h1>
<div>
<label>username</label>
<input
type="text"
name="userName"
onChange={handleChange}
value={values.userName}
required
/>
</div>
<div>
<label>Date of Birth</label>
<input
type="date"
name="dateOfBirth"
onChange={handleChange}
value={values.dateOfBirth || ""}
/>
</div>
<div>
<input type="submit" value="Submit" />
</div>
</form>
</>
);
};
export default App;
Questions:
Is addSubmission setup properly for what I am trying to achieve? Or can it be improved?
Base on console.log() placement, why am I only seeing previous submissions and not current? Screenshot below for details.
Your addSubmission isn't consistent, you initialize it as an array of Objects, but when you add a submission to it you are adding an extra value key, as seen in your second log, where the value at index 1 is an object with a key of value. To resolve this, simply remove the bracket
const newSubmissions = [...submissions, values];
You should also clear the current state values after you have successfully submitted an entry.
SetState is async, which means React might still be updating the state when your console.log is called, and that's why you wouldn't see the current submission, if you place the console.log outside of the function and have it triggered on rerender you would see the new submission.

Display multiple dynamic input fields in react, based on a changing state variable?

So, I am building my first React app, that connects to a Flask backend. I have a specific functional component that fetches a dictionary from the Python program, and I have saved this in a state variable(done using useState, setVariables).
The object is of type
{'A':'x', 'B':'y', 'C':2},
and these are variable, and each render needs to be dynamic, based only on this object.
Based on the current state of this object, I want to display key-value pairs of this object in
<label>insertkey</label>
<input type="text" name = "insertkey" value=insertvalue/>
In addition, I want to be able to save any changes made to these values, and write into the state variable, so that I can send them back to my Flask backend.
I tried looping through each, and inserting as follows:
const [fields, setFields] = useState({});
//intermediate code for fetching and setting the state
useEffect(() => {
for (const [key, value] of Object.entries(fields)) {
console.log(`${key}:${value}`);
insert = insert + `<label> ${key} </label> <input type = 'text' value = ${value} name = ${key} onChange=${changedData()}></input><br/>`
}
document.getElementById('fields_form').innerHTML = insert;
});
const changedData = (evt) => {
setFields({ [evt.target.name]: evt.target.value });
console.log(evt.target.value);
}
//some intermediate code
return (
<div>
<form onSubmit={submitFunction}>
<p id='fields_form'>
</p>
<input type="submit" value="Submit"></input>
</form>
</div>
);
But when I do this, the state is set as 'Undefined'.
I would love to hear about some ways to implement this. Maybe it's a really small fix, and I'm missing it, or perhaps there is a different approach that could be more suitable.
Simple:
const Form = () => {
const [values, setValues] = useState({});
const onChange = (e) => {
const name = e.target.name;
let value = e.target.value;
setValues({ ...values, [name]: value });
};
const onSubmit = (e) => {
// send data or save it to local store
api.send(values)
};
return (
<form encType="multipart/form-data">
<input
type="text"
name="email"
value={value["email"]}
onChange={onChange}
/>
<input
type="text"
name="subject"
value={value["subject"]}
onChange={onChange}
/>
<input
type="text"
name="body"
value={value["body"]}
onChange={onChange}
/>
>
<button type="submit" onClick={onSubmit}>
Save
</button>
</form>
);
};
export default Form;
console.log(values);
It's your solution.
const [fields, setFields] = useState({});
const [values, setValues] = useState({});
// some fetch logic
// happens on loading some data
// for example data structure look's like:
// fetchedData = {
// email: 'email#placeholder'
// subject: 'subject placeholder'
// body: 'body placeholder'
// }
useEffect(() => {
if (fetchedData) {
setFields(fetchedData)
}
}, [fetchedData]);
// happens on fields changed
useEffect(() => {
if (fields) {
setValues(fields)
}
}, [fields]);
const changedData = (evt) => {
setValues({ ...values, [evt.target.name]: evt.target.value });
}
return (
<div>
<form onSubmit={submitFunction}>
{Object.keys(fields).map(_ => (
<input
type="text"
name={_}
value={values[_]}
onChange={changedData}
/>
))}
<input type="submit" value="Submit"></input>
</form>
</div>
);

How to change JSON data through textboxes and buttons in a ReactJS?

I just started learning react.
Please see code in codepen link below.
When you press the edit button, the field in the table is changed to a textbox.
And that's what I want.
I wanna give Click on the edit button again.
How to replace the value of the data in JSON data?
Thanks so much for the help.
let UsersData = [
{Name: 'AAA',Last:"1111"},
{Name: 'BBBB',Last:"222"},
]
constructor(props) {
super(props)
this.state={
Editing:false,
}
this.toggleEditing = this.toggleEditing.bind(this)}
toggleEditing() {
let Editing = !this.state.Editing
this.setState(
{Editing: Editing}
)}
FULL CODE IN CODEPEN
Codepen https://codepen.io/StCrownClown/pen/MEQPzP?editors=0010
To change your JSON data first you need to get the user input through your TextInput component, to do that you need to define a value and an onChange props to store the value of the input in your state. Given that your input is a custom component I'll pass those props as props.
Like this:
class TextInput extends React.Component {
render() {
const {value, onChange, name} = this.props
return (
<td>
<input type="text"
value={value} // to display the value
onChange={onChange} // to store the value on the state
name={name} // to use use the name as a property of the state
/>
</td>
)
}
}
Then in your TableRow component state, you need to:
Save those value and handle their changes:
this.state = {
Editing:false,
// from props to show their current value
name : this.props.data.Name
last: this.props.data.Last
}
// to handle changes
onChange(event){
this.setState({
[event.target.name] : event.target.value
})
}
and pass the above mentioned props to the TextInput:
<TextInput value={this.state.name} name="name" onChange={this.onChange}></TextInput>
<TextInput value={this.state.last} name="last" onChange={this.onChange} ></TextInput>
To show those values to the user when to Editing is false you need to:
Defined a state for your Table component so it re-renders with the changes and a function that changes that state when the user is done editing:
this.state = {
UsersData: UsersData
}
saveChanges({key, name, last}){
// key: unique identifier to change the correct value in the array
// name: new Name
// last: new Last
this.setState(prevState => ({
UsersData: prevState.UsersData.map(data => {
if(data.Name === key) return { Name: name, Last: last }
return data
})
}))
}
Finally, pass that function to the TableRow component:
const rows = []
// now the loop is from the UsersData in the component state to see the changes
this.state.UsersData.forEach((data) => {
rows.push (
<TableRow
key={data.Name}
saveChanges={this.saveChanges}
data={data}
/>
)
})
and call the saveChanges function in the TableRow component when the Done button is clicked:
saveChanges(){
const {name , last} = this.state
this.toggleEditing()
this.props.saveChanges({
key: this.props.data.Name,
name,
last
})
}
<button onClick={this.saveChanges} >Done</button>
You can check the full code here.

How to reset ReactJS file input

I have file upload input:
<input onChange={this.getFile} id="fileUpload" type="file" className="upload"/>
And I handle upload this way:
getFile(e) {
e.preventDefault();
let reader = new FileReader();
let file = e.target.files[0];
reader.onloadend = (theFile) => {
var data = {
blob: theFile.target.result, name: file.name,
visitorId: this.props.socketio.visitorId
};
console.log(this.props.socketio);
this.props.socketio.emit('file-upload', data);
};
reader.readAsDataURL(file);
}
If I upload same file twice, then upload event is not fired. How can I fix that? For simple js code it was enough to do the following: this.value = null; in change handler. How can I do it with ReactJS?
I think you can just clear the input value like this :
e.target.value = null;
File input cannot be controlled, there is no React specific way to do that.
Edit For old browsers (<IE11), you can use one of the following techniques.
See http://jsbin.com/zurudemuma/1/edit?js,output (tested on IE10 & 9)
What worked for me was setting a key attribute to the file input, then when I needed to reset it I update the key attribute value:
functionThatResetsTheFileInput() {
let randomString = Math.random().toString(36);
this.setState({
theInputKey: randomString
});
}
render() {
return(
<div>
<input type="file"
key={this.state.theInputKey || '' } />
<button onClick={this.functionThatResetsTheFileInput()} />
</div>
)
}
That forces React to render the input again from scratch.
This work for me - ref={ref => this.fileInput = ref}
<input id="file_input_file" type="file" onChange={(e) => this._handleFileChange(e)} ref={ref=> this.fileInput = ref} />
then in my case once the file was uploaded to the server , I clear it by using the statement below
this.fileInput.value = "";
I do it by updating key inside my file input.
This will force a re-render and previously selected file will go away.
<input type="file" key={this.state.inputKey} />
Changing the state inputKey will re-render the component.
One way to change the inputKey will be to always set it to Date.now() on click of a button which is supposed to clear the field.
With every click onClick you can reset the input, so that even with the same file onChange will be triggered.
<input onChange={this.onChange} onClick={e => (e.target.value = null)} type="file" />
import React, { useRef } from "react";
export default function App() {
const ref = useRef();
const reset = () => {
ref.current.value = "";
};
return (
<>
<input type="file" ref={ref} />
<button onClick={reset}>reset</button>
</>
);
}
The following worked for me using React Hooks. This is done using what is known as a "controlled input". That means, the inputs are controlled by state, or their source of truth is state.
TL;DR Resetting the file input was a two-step process using both the useState() and useRef() hooks.
NOTE: I also included how I reset a text input in case anyone else was curious.
function CreatePost({ user }) {
const [content, setContent] = React.useState("");
const [image, setImage] = React.useState(null); //See Supporting Documentation #1
const imageInputRef = React.useRef(); //See Supporting Documentation #2
function handleSubmit(event) {
event.preventDefault(); //Stop the pesky default reload function
setContent(""); //Resets the value of the first input - See #1
//////START of File Input Reset
imageInputRef.current.value = "";//Resets the file name of the file input - See #2
setImage(null); //Resets the value of the file input - See #1
//////END of File Input Reset
}
return (
<div>
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Add Post Content"
onChange={event => setContent(event.target.value)}
value={content} //Make this input's value, controlled by state
/>
<input
type="file"
onChange={event => setImage(event.target.files[0])} //See Supporting Doc #3
ref={imageInputRef} //Apply the ref to the input, now it's controlled - See #2
/>
<button type="submit">Submit Form</button>
</form>
</div>
)
};
Supporting Documentation:
useState Hook
Returns a stateful value, and a function to update it.
useRef Hook
If you pass a ref object to React, React will set its current property to the corresponding DOM node whenever that node changes.
Using files from web apps
If the user selects just one file, it is then only necessary to consider the first file of the list.
You can also include this in your input element if you know you are not going to be using the built-in file input value at all.
<input value={""} ... />
This way the value is always reset to the empty string on render and you don't have to include it awkwardly in an onChange function.
I know file input is always uncontrolled however the following code still works in my own porject, I can reset the input with no problems at all.
constructor(props) {
super(props);
this.state = {
selectedFile: undefined,
selectedFileName: undefined,
imageSrc: undefined,
value: ''
};
this.handleChange = this.handleChange.bind(this);
this.removeImage = this.removeImage.bind(this);
}
handleChange(event) {
if (event.target.files[0]) {
this.setState({
selectedFile: event.target.files[0],
selectedFileName: event.target.files[0].name,
imageSrc: window.URL.createObjectURL(event.target.files[0]),
value: event.target.value,
});
}
}
// Call this function to reset input
removeImage() {
this.setState({
selectedFile: undefined,
selectedFileName: undefined,
imageSrc: undefined,
value: ''
})
}
render() {
return (
<input type="file" value={this.state.value} onChange={this.handleChange} />
);
}
We can reset file input by using key = {this.state.fileInputKey} and initialsing fileInputKey to Date.now() in constructor state.
On file upload success , we need to again assign fileInputKey: Date.now(), so it will have different value than previous and it create new file input component on next render()
We can also do this manually by clicking button to clear/reset file Input
Below is the working code :
import React from "react";
import { Button } from "reactstrap";
class FileUpload extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedFile: null,
fileInputKey: Date.now(),
message: ""
};
this.handleClear = this.handleClear.bind(this);
this.onClickHandler = this.onClickHandler.bind(this);
this.onChangeHandler = this.onChangeHandler.bind(this);
}
onChangeHandler = event => {
this.setState({
selectedFile: event.target.files
});
};
onClickHandler = () => {
if (this.state.selectedFile === null) {
this.setState({
message: "Please select File"
});
return;
}
//axios POST req code to send file to server
{
/**
const data = new FormData()
data = this.state.selectedFile[0]
axios.post("http://localhost:8080/api/uploadFile/", data)
.then(res => {
if (res.status == 200) {
// upload success
}
})
.catch(err => {
//message upload failed
})
*/
}
//after upload to server processed
this.setState({
selectedFile: null,
fileInputKey: Date.now(),
message: "File Uploaded"
});
};
handleClear() {
this.setState({
selectedFile: null,
fileInputKey: Date.now(),
message: ""
});
}
render() {
return (
<div>
<input
type="file"
key={this.state.fileInputKey}
class="form-control"
onChange={this.onChangeHandler}
/>
<button
type="button"
class="btn btn-success btn-block"
onClick={this.onClickHandler}
>
Upload
</button>
<Button
type="button"
value="Clear"
data-test="clear"
onClick={this.handleClear}
>
{" "}
Clear{" "}
</Button>
<br />
<label>{this.state.message}</label>
</div>
);
}
}
export default FileUpload;
Here is my solution using redux form
class FileInput extends React.Component {
constructor() {
super();
this.deleteImage = this.deleteImage.bind(this);
}
deleteImage() {
// Just setting input ref value to null did not work well with redux form
// At the same time just calling on change with nothing didn't do the trick
// just using onChange does the change in redux form but if you try selecting
// the same image again it doesn't show in the preview cause the onChange of the
// input is not called since for the input the value is not changing
// but for redux form would be.
this.fileInput.value = null;
this.props.input.onChange();
}
render() {
const { input: { onChange, value }, accept, disabled, error } = this.props;
const { edited } = this.state;
return (
<div className="file-input-expanded">
{/* ref and on change are key properties here */}
<input
className="hidden"
type="file"
onChange={e => onChange(e.target.files[0])}
multiple={false}
accept={accept}
capture
ref={(input) => { this.fileInput = input; }}
disabled={disabled}
/>
{!value ?
{/* Add button */}
<Button
className="btn-link action"
type="button"
text="Add Image"
onPress={() => this.fileInput.click()}
disabled={disabled}
/>
:
<div className="file-input-container">
<div className="flex-row">
{/* Image preview */}
<img src={window.URL.createObjectURL(value)} alt="outbound MMS" />
<div className="flex-col mg-l-20">
{/* This button does de replacing */}
<Button
type="button"
className="btn-link mg-b-10"
text="Change Image"
onPress={() => this.fileInput.click()}
disabled={disabled}
/>
{/* This button is the one that does de deleting */}
<Button
type="button"
className="btn-link delete"
text="Delete Image"
onPress={this.deleteImage}
disabled={disabled}
/>
</div>
</div>
{error &&
<div className="error-message"> {error}</div>
}
</div>
}
</div>
);
}
}
FileInput.propTypes = {
input: object.isRequired,
accept: string,
disabled: bool,
error: string
};
FileInput.defaultProps = {
accept: '*',
};
export default FileInput;
In my case I had a functional component and after selecting a file it suppose to set the file name in the state so using any solution above was failing except the ref one which i fixed like this.
const fileUpload = props => {
const inputEl = useRef(null)
const onUpload = useCallback(e => {
uploadFile(fileDetails)
.then(res => {
inputEl.current.value = ''
})
.catch(err => {
inputEl.current.value = ''
})
})
return (
<input type='file' ref={inputEl} onChange={handleChange} />
<Button onClick={onUpload}>Upload</Button>
)
}
I recently got stumbled into this issue to reset the File type input field. I think it is still a milestone for most developers. So I thought I should share my solution.
Since we are listening to the onChange event to update the image file into some of our states, we will have our component rerendered once we set the state. In such case, we can specify the value of the input file as empty like value='' which will cause the input field to reset its value after each change of its value.
<input
type="file"
value=''
onChange={onChangeFnc}
/>