I am trying to fetch data from an API from an input. After providing the input and calling the data, I am getting two fetch call from same data. here is the code of input:
const [countryName, setCountryName] = useState<string>("");
const handleInputChange = (event: {
target: { value: React.SetStateAction<string> };
}) => {
setCountryName(event.target.value);
};
const onSubmit = () => {
navigate(`/country/${countryName}`);
};
<TextField
variant="standard"
placeholder="Enter country Name"
value={countryName}
onChange={handleInputChange}
/>
Here I am using the input to fetch the data:
const { name } = useParams();
const [loading, setLoading] = useState<boolean>(false);
const [country, setCountry] = useState<InitCountry>();
useEffect(() => {
const getCountry = async () => {
setLoading(true);
const response = await fetch(
`https://restcountries.com/v3.1/name/${name}`
);
const data = await response.json();
console.log(data);
setCountry(data.length > 1 ? data[2] : data[0]);
setLoading(false);
};
getCountry();
}, [name]);
What am I doing wrong here?
Your setup seems good, but the root cause is possibly from React.StrictMode
Strict mode can’t automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following functions: ...
This effect is only in development which means it won't happen in your production, but if you don't want intentionally double-invoking, you can remove it from your code.
i want to add data after uploading 1 image
const [file, setFile] = useState(null);
const handleUpload = (e) => {
setFile(e.target.files);
};
<input
type="file"
onChange={(e) => handleUpload(e)}
ref={fileInputRef}
multiple
/>
this was work if i select 2 or more files at once.
however i want to handle after choosing 1 image. user will be upload using the same input.
i have tried using method below and created error TypeError: file is not iterable
const handleUpload = (e) => {
setFile([...files, e.target.files]);
};
or
const handleUpload = (e) => {
let filesArray = file;
if (file) {
for (var i = 0; i < file.length; i++) {
filesArray.push(e.target.files[i]);
}
}
setFile(filesArray);
};
do you guys have any solution?
You should first set the default value for file as an array in your useState, then while using the set method you need to use the spread (...) operator properly. This will resolve your error TypeError: file is not iterable
Below are the changes that you will need to perform.
export default function InputComponent() {
const [file, setFile] = React.useState([]);
const handleUpload = (e) => {
setFile([...file, ...e.target.files]);
};
return (
<div>
<input type="file" onChange={(e) => handleUpload(e)} multiple /><br/><br/>
<textarea value={file}/>
</div>
);
}
In react
const allInputs = { imgUrl: '' };
const [imageAsFile, setImageAsFile] = useState('');
const [imageAsUrl, setImageAsUrl] = useState(allInputs);
const [media, setMedia] = useState(null);
const handleImageAsFile = (e) => {
const image = e.target.files[0]
setImageAsFile(imageFile => (image));
console.log(imageAsFile);
}
Here is the input code, when I click this button, all types of files show, but I want to be able to store the type of file it is in a variable
<input type="text"
id="phone"
onChange={(e) => setPhone(e.target.value)}
/>
For example, if I select an image, how can know the type of image I have selected? If it is png or jpg or whatever before uploading it to the database.
First, React Javascript works the exact way Javascript works everywhere, For accessing the image type refer to the below code, I have added the comments for better understanding.
const [imageAsFile, setImageAsFile] = useState('');
const [imageAsUrl, setImageAsUrl] = useState(allInputs);
const [media, setMedia] = useState(null);
const handleImageAsFile = (e) => {
//image var holds the file object which has a type property
const image = e.target.files[0];
console.log(image.type); // this will output the mime, i.e "image/png" or "image/jpg"
setImageAsFile(imageFile => (image));
console.log(imageAsFile);
}
So I am working on my React app and a one point i need to upload some files. So I simply used the input file to make it work. I set its display none as I wanted my attachment icon to be clicked when wanting to upload the file.
Problem : Using the ref method, everything is working fine except one thing and that is below in my hangleFileChange function, when the setFiles() set the file variable, the component is not rendered and I do not see the file array. but if I do the file saving simply like
setFile(event.target.files[0])
I can see the rendering . But with the below code, the component is not rendering
import React, { useRef, useState } from "react";
const App = () => {
const fileInput = useRef(null);
const [file, setFile] = useState([]);
const handleClick = () => {
fileInput.current.click();
};
const handleFileChange = (event) => {
console.log("Make something");
let newFiles = file;
newFiles.push(event.target.files[0]);
console.log(newFiles);
setFile(newFiles);
};
// This should run on every render
console.log("the files array is ", file);
return (
<div className="patientactions-container">
<input
type="file"
style={{ display: "none" }}
onChange={(e) => handleFileChange(e)}
ref={fileInput}
/>
<div onClick={() => handleClick()}>clck</div>
</div>
);
};
export default App;
Please help.
Sandbox : https://codesandbox.io/s/kind-breeze-czc3w?file=/src/App.js:0-692
Try this version
const handleFileChange = (event) => {
console.log("Make something");
// Set the ne variable to an array, not file
let ne = [];
ne.push(event.target.files[0]);
// then set it equals file.
ne = file;
console.log(ne);
console.log(file);
setFile(file);
};
You can fix the code like this below.
import React, { useRef, useState } from "react";
const App = () => {
const fileInput = useRef(null);
const [file, setFile] = useState(null);
const handleClick = () => {
fileInput.current.click();
};
const handleFileChange = (nfile) => {
console.log("Make something");
if (file == null) setFile([nfile]);
else setFile([...file, nfile]);
};
console.log("the files array", file);
return (
<div className="patientactions-container">
<input
type="file"
style={{ display: "none" }}
onChange={(e) => handleFileChange(e.target.files[0])}
ref={fileInput}
/>
<div onClick={() => handleClick()}>clck</div>
</div>
);
};
export default App;
I'd have put this in the comments but my rep is not high enough.
I had a problem with rendering changes to an array, because arrays use pointers it did not "register" a state change that was enough to cause a render. Using the spread operator in your solution affected the pointer and thus a render occurred.
In my own solution I set my array to null before adding content and that worked fine for my problem.
I am learning react-hooks, I created a series of state variables using useState, when trying to debug and see its value I find that React Developer Tool does not show the name assigned to the state variable but the text State, this is inconvenient as it is not possible to identify from the beginning which variable is the one that is tried to debug
Update 1
This is the current source code
import React, { useState, useEffect, Fragment } from "react";
function Main() {
const [data, setData] = useState({ hits: [] });
const [query, setQuery] = useState("redux");
const [url, setUrl] = useState(
"https://hn.algolia.com/api/v1/search?query=redux"
);
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
useEffect(() => {
const fetchData = async () => {
setIsError(false);
setIsLoading(true);
try {
const response = await fetch(url);
const json = await response.json();
setData(json);
} catch (e) {
setIsError(true);
}
setIsLoading(false);
};
fetchData();
}, [url]);
return (
<Fragment>
<form
onSubmit={event => {
setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`);
event.preventDefault();
}}
>
<input value={query} onChange={event => setQuery(event.target.value)} />
<button type="submit">Search</button>
</form>
{isError && <div>Something went wrong ...</div>}
{isLoading ? (
<div>Loading...</div>
) : (
<ul>
{data.hits.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
)}
</Fragment>
);
}
export default Main;
I am getting this in React Developer tool
Updated 2
I am using Firefox 68
Is it possible that React Developer Tool shows the name of state variables created using useState?
See this issue:
https://github.com/facebook/react-devtools/issues/1215#issuecomment-479937560
That's the normal behavior for the dev tool when using hooks.
I asked the library author about it, cause I also would like it to show my state variable names. And that's what he said:
#cbdeveloper I haven't thought of a good way for DevTools to be able to display the variable name like you're asking. DevTools doesn't have a way to read your function's private variables, and changing the API to support passing in a display name would increase the size of component code. It also just doesn't seem necessary to me, more like a nice to have.
Anyway, this umbrella issue is not the best place to have discussions like this. If you feel strongly about it, I suggest opening a new issue.
From "normal" useState hook implementation:
const [users, setUser] = useState([]);
const [profile, setProfile] = useState([]);
const [repo, setRepo] = useState([]);
const [loading, setLoading] = useState(false);
const [alert, setAlert] = useState(false);
You can "convert" it in:
const [state, setState] = useState({ users: [], profile: [], repo: [], loading: false, alert: false });
And you will get the following result:
And to set the state you can use the rest operator(source 1 / source 2) and the state you want to set:
// ...state => unchanged states
// alert => state we want to change
setState(state => ({ ...state, alert: true }));
to use it as a prop:
const {
users,
profile,
repo,
loading,
alert
} = state;
<SomeComponent loading={loading} alert={alert} />
You see the React docs here and search for: Should I use one or many state variables?
You can use useDebugValue to display a custom label in your own hook:
const format = ({ label, value }) =>
label + ': ' + (typeof value === 'object' ? JSON.stringify(value) : value);
const useNamedState = (label, initialState) => {
const states = useState(initialState);
useDebugValue({ label, value: states[0] }, format);
return states;
};
Usage
Before
const [name, setName] = useState('bob');
const [age, setAge] = useState(11);
const [address, setAddress] = useState('London, United Kingdom');
const [isVirgin, setIsVirgin] = useState(false);
const [isMale, setIsMale] = useState(true);
const [hobbies, setHobbies] = useState(['gaming', 'hiking', 'cooking']);
const [friendList, setFriendList] = useState(['bob']);
After
const [name, setName] = useNamedState('name', 'bob');
const [age, setAge] = useNamedState('age', 11);
const [address, setAddress] = useNamedState('address', 'London, United Kingdom');
const [isVirgin, setIsVirgin] = useNamedState('isVirgin', false);
const [isMale, setIsMale] = useNamedState('isMale', true);
const [hobbies, setHobbies] = useNamedState('hobbies', ['gaming', 'hiking', 'cooking']);
const [friendList, setFriendList] = useNamedState('friendList', ['bob']);
Hmm, I'm not sure why it doesn't show the names, but if I'm trying to create a series of state variables, I sometimes just initialize a state and setState variable as an empty object. I'm not sure if this is the best way, but maybe in your dev tools, it'll show the state attribute name when you expand it.
import React from "react";
export default function App(props) {
// Initializing the state
const [state, setState] = React.useState({});
// Changing the state
setState({ ...state, text1: "hello world", text2: "foobar" });
return (
<div>
{state.text1}
<br />
{state.text2}
</div>
);
}