Storing uploaded file in state variable wont work. React - reactjs

I am trying to store an uploaded file in a state variable that I will post to the DB once the user has completed the rest of the form. For some reason, my state wont update and my state variable is left null. I am calling onChange and passing it my onChange function to store the file in state. I put some comments in the code. Can anyone help please?
interface PostState {
file: File
//i have tried multiple data types for the file variable, still no luck
}
const Form = (props) => {
const defaultPostState: PostState = {
file: null,
//i have also tried setting this to undefined or not setting a default value at all as well
}
const [postState, setPostState] = useState(defaultPostState)
const onChange = (e) => {
setPostState({...postState, file: e.target.files[0]})
//this is what wont work - i have debugged it and logged it, it wont store the target file in file
}
<div className='form-group'>
<label>Upload file</label>
<input
id='file'
className='form-control-file'
type='file'
name='file'
ref={register}
onChange={onChange}
/>
</div>

Related

React File Upload

I just want to create simple file upload component on react. It will be something simple that after select the file and submit the upload button, it will give a massage with filename like "You uploaded fileName.txt". I have tried search so many sources but it's mostly more than I need, that would be great if anyone could help me about it. Thank you!
You can detect file selection using the onChange event listener, and you can access the name of the selected file via e.target.files which returns all of the files which have been selected, we'll take the first selected file and store its name value inside a state called selectedFileName.
during render we'll check if the selectedFileName state has value, we'll display it inside a p tag.
import React, { useState } from 'react';
export const App = () => {
const [selectedFileName, setSelectedFileName] = useState('');
const [displayFileName, setDisplayFileName] = useState(false);
const submitFile = () => setDisplayFileName(true);
const handleFileSelect = e => {
const selectedFile = e.target.files[0];
setDisplayFileName(false);
setSelectedFileName(selectedFile?.name);
};
return (
<div>
<input
type='file'
accept='*'
name='fileSelect'
onChange={handleFileSelect}
/>
{displayFileName ? (
<p>{ selectedFileName?`You Uploaded '${selectedFileName}'`:"Please select a file!"}</p>
) : (
<button value='Submit' onClick={submitFile}>
Submit File
</button>
)}
</div>
);
};

How do I load API data into input value property on page load from React State?

I have attained some data from an API and stored it in a state.
const [latestNumber, setLatestNumber] = useState([])
This is what is contained in the state
{
"KeyID": 4676,
"ApplicationNo": 607
}
I would want to insert that data into a READ ONLY input field, so taking latestNumber.KeyID from my API and adding it as the value property like so: <Form.Control value={latestNumber.KeyID}>.
The key here is that I want the value to load upon the page load, not onChange.
Currently the input does not display the data on load of the page.
Here is what I have tried.
const [latestNumber, setLatestNumber] = useState([])
useEffect(()=>{
axios
.all([fetchData1, fetchData2])
.then(axios.spread((...responses)=>{
console.log(responses)
setLatestNumber(responses[1].data)
})).catch(error=>{console.log(error)})
}, [setLatestNumber]);
The component
<Form.Group as={Col} className="mb-1" controlId="formBasicDetail">
<Form.Label>KeyID</Form.Label>
<Form.Control type="text" readOnly value={latestNumber.KeyID} onChange={(e)=>setLatestNumber({KeyID: e.target.value})} />
</Form.Group>
Managed to solve the issue. Firstly you cannot setState directly on the array, for some reason because latestNumber only contains 1 object, you will be unable to access the object subproperties.
The API I am concerned with is the secondResponse.
Do as such:
useEffect(()=>{
const fetchData = async()=>{
const firstResponse = await axios.get(`http://localhost:3002/api/users/${props.match.params.employee_id}`);
const secondResponse = await axios.get("http://localhost:3002/api/latestnumber")
setLeaves(firstResponse.data)
setLatestNumber(secondResponse.data[0])
console.log(secondResponse.data)
}
fetchData()
}, []);
The important part is how you set the state:
setLatestNumber(secondResponse.data[0])
DO NOT DO THIS, does not work:
setLatestNumber(secondResponse.data)
//followed by setting this as the input value
latestNumber[0].KeyID
To then add it into your input, do as following:
<Form.Control type="text" readOnly value={latestNumber.KeyID} name="KeyID" onChange={handleChange} />
handleChange is just a generic handler to update the overall state of the Form

Hooks not setting the state in REACT

In the following code, the hook shown below does not change the state of the variable newName.
import React, { useState } from 'react'
const App = () => {
const [ persons, setPersons] = useState([
{ name: 'Arto Hellas' }
])
const [ newName, setNewName ] = useState('')
const textChangeHandler = (event)=>{
event.preventDefault()
setNewName(event.target.value) // WORKS FINE
}
const submitHandler = (event)=>{
event.preventDefault()
let temp = {name:newName}
setNewName('') //////////////////////////////////////// PROBLEM - doesnot set state!!
console.log('tenp name is',temp.name)
console.log('new name is',newName)
setInterval(()=>console.log("Set Interval",newName), 1000)
}
return (
<div>
<h2>Phonebook</h2>
<form onSubmit={submitHandler}>
<div>
name: <input onChange={textChangeHandler} />
</div>
<div>
<button type="submit" >add</button>
</div>
</form>
<h2>Numbers</h2>
{persons.map((person) => <Person key = {person.name} name={person.name}/> )}
</div>
)
}
const Person = ({name})=> <p> {name} </p>
setNewName works fine and the name is updated when anything is typed in the input box. However, when I submit button, the setNewName does not seem to work. Even after updating, executing the setNewName the name is still the old name.
I have even tried a setInterval (thinking it may be due to asynchronous nature of JS) and printed the newName but, it still shows the old name.
What is the problem and how can it be fixed?
thanks
If you look at your submitHandler function, you will notice that the name its self has not changed... it remains empty as in its initial state. React only reload if the state value changes as shown below
const submitHandler = (event)=>{
event.preventDefault()
let temp = {name:newName}
//setNewName('') // your value is not changing here ... removing line
setNewName(temp.name) // changing the name here with the one in temp
console.log('tenp name is',temp.name)
console.log('new name is',newName)
setInterval(()=>console.log("Set Interval",newName), 1000)
}
As you know setting a state is asynchronous. Never use scheduling functions like setTimeout setInterval to log out the value of state.
Use useEffect instead -
useEffect(() => {
console.log("value is", newName);
}, [newName]);
You hook is working fine without a problem. Here's a codeSandbox showing everything's working fine.
In React setState is not synchronous ( with class components and hooks ).
So you can not expect that a new state will be available immediately.
You need to change the implementation of the submitHandler function.

_react2.fireEvent.change is not a function | react testing library

I try to simulate onChange with fireEvent.change, but get error _react2.fireEvent.change is not a function.
I checked official docs of react testing library, did everything exactly how it's there https://testing-library.com/docs/example-input-event, even created another test where copied all code from docs, and created exactly the same input in react component. Still the same - _react2.fireEvent.change is not a function.
My input
<input
data-testid='input-file'
value={img}
onChange={upImage}
className="file-upload"
type="file"
/>
my Test
it('img preview', () => {
const form = render(<Form addItem={func} />)
const input = form.getByTestId('input-file')
const file = new File(['dummy content'], 'example.png', {type: 'image/png'})
fireEvent.change(input, { target: { value: { file } } })
})
also, when I check input in test, in console it's showed like this:
Received: <input class="file-upload" data-testid="input-file" type="file" value="" />
there is simply not onChange

UI not re-rendering on state update using React Hooks and form submission

I'm trying to update a UI using React Hooks and a form. I have a state set to monitor the value on the form and when I click submit, I want to add this value to an array (held in state) and display it on the UI. My problem is that when I submit the value, although it is added to the array (and state is updated), the UI only updates when I change the value in the input.
My Component is as follows:
const PushToArrayUpdateState = () => {
const [array, setArray] = useState([]);
const [formVal, setFormVal] = useState(``);
const handleSubmit = event => {
event.preventDefault();
let updateArray = array;
updateArray.push(formVal);
console.log(updateArray);
setArray(updateArray);
};
return (
<div>
<form onSubmit={handleSubmit}>
<input type="text" name="arrayVal" onChange={e => setFormVal(e.target.value)} />
<input type="submit" value="Submit" />
</form>
<div>
{array.map((val, index) => <p key={index}>{val}</p>)}
</div>
</div>
);
};
You can also see this [not] working at:
https://codesandbox.io/s/p3n327zn3q
Has anyone got any suggestions as to why the setArray in the handleSubmit function is not automatically causing the component to re-render?
Instead of
let updateArray = array;
Try this:
const updateArray = [...array];
https://codesandbox.io/embed/qxk4k3zmzq
Because arrays in JS are reference values, so when you try to copy it using the = it will only copy the reference to the original array.
A similar bug can happen with the same manifestation:
const [bought, setBought] = useState([])
...
bought.push(newItem)
setBought(bought)
To fix this you need to use
const newBought = [...bought, newItem] <- new reference
setBought(newBought)

Resources