Unable to save input picture data in state variable React.js - reactjs

i'm trying to learn how the image upload process works with react. i have an input that takes image data in and a handler that sets that information to a variable but i am doing something incorrect and not sure what it is.
I want to update picture in state with the picture information and leave the other items in state alone. When i do the below, and console.log after the state of picture stays null.
class Profile extends Component {
constructor() {
super();
this.state = {
userName: "",
userEmail: "",
picture: null
};
this.handleNewImage = this.handleNewImage.bind(this);
}
handleNewImage = event => {
this.setState({
picture: event.target.files[0]
})
console.log(this.state.picture); //gives null still
}
render() {
return (
<input type='file' onChange={this.handleNewImage} />
);
}
}

This probably has nothing to do with images but rather an understanding of setState. React's setState is an async operation so you'd need to console log once the state update is complete:
handleNewImage = event => {
this.setState({
picture: event.target.files[0]
}, () => {
console.log(this.state.picture);
});
}
More info here:
https://reactjs.org/docs/react-component.html#setstate
https://css-tricks.com/understanding-react-setstate/

Related

React state value won't update

I'm trying to update state and set preview value to undefined, but it's not working.
here is my code
console.log(preview);
this.setState({ preview }, () => {
console.log(this.state.preview);
});
I try to put something else in preview, but still it's not working.
undefined
Object { ... }
other value in state is working, just this one it's not working.
UPDATE
another weird thing is that when I update another value it will be updated.
for example
this.setState({preview:undefined,media:undefined});
the media will updated to undefined, but preview will not.
React state updates are asynchronous and batched processed for the next render/commit cycle. What this means then is if any other part of the code also is setting the same state to some value after an update, that update is lost.
Demo
class App extends Component {
state = {
value: "test"
};
updateState = () => {
this.setState({ value: undefined }, () =>
console.log("value", this.state.value) // logs "test2"!!
);
this.setState({ value: "test2" });
};
render() {
return (
<div>
<div>state: {this.state.value}</div>
<button type="button" onClick={this.updateState}>
Update state
</button>
</div>
);
}
}

I wanna console.log the value after clicking the submit button once and to delete the previous mapped items, but it doesnt work

I'm very new to react and I got two problems:
I want to console log the input and display the mapped data after clicking the submit button once. But I get console logged the input and the mapped data after clicking the button twice.
I wanna clear the mapped list (data from previous input) and display new list items depending on the input. But the new list items are only added to the end of the previous list (only the last list item from the previous list got overwritten by the first list item of the new list).
So this is the code from my app component:
import React, { Component, Fragment } from 'react';
import './App.css';
import Display from './/Display';
class App extends Component {
constructor(props) {
super(props);
this.state = {
value: "",
passedValue: ""
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({ value: event.target.value });
}
handleSubmit(event) {
this.setState({ passedValue: this.state.value });
console.log(this.state.passedValue);
event.preventDefault();
}
render() {
return (
<div>
<form className="inputContainer" onSubmit={this.handleSubmit}>
<input type="text" name="company_name" onChange={this.handleChange} />
<input type="submit" value="Submit" />
</form>
<Display listDataFromParent={this.state.passedValue} />
</div>
);
}
}
export default App;
And this is my display component:
import React, { Component } from 'react'
import "./Display.css";
export default class Display extends Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
data: []
};
}
componentWillReceiveProps() {
fetch("http://localhost:5000/company?company_name=" + this.props.listDataFromParent)
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
data: result
});
},
// Note: it's important to handle errors here
// instead of a catch() block so that we don't swallow
// exceptions from actual bugs in components.
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
render() {
const { error, isLoaded, data } = this.state;
// if (error) {
// return <div>Error: {error.message}</div>;
// } else if (!isLoaded) {
// return <div>Loading...</div>;
// } else {
return (
<div className="display">
<h1>Kreditnehmer</h1>
<ul>
{this.props.listDataFromParent}
{data.map(item => (
<li key={item.c.company_id}>
Relation type: {item.r.relation_group}
Last name: {item.p.last_name}
</li>
))}
</ul>
</div>
);
}
}
Can anyone help?
1) setState is async method in react means it will take some time to update the component state. You can get your console log by using callback function of setState like
this.setstate({ value: e.target.value }, () => { console.log(this.state.value) });
2) in display component, your using componentWillReciveProps life cycle and inside that your using this.props.listdatafromparent which is pointing previous props. Rather than using this.props I would suggest consider props param of life cycle, means it should be like
componentWillReciveProps(props) {
// your code
Console.log(props.listdatafromparent);
}
The handleSubmit method is wrong... the console log is executed before the state is changed. You need to put it inside a callback function as a second parameter of setState.
this.setState({ passedValue: this.state.value }, () => {
console.log(this.state.passedValue);
});
Answers are:
1) Callback function should be used on setState, in order to do console.log after state is really updated.
In your case you call setState and setState is async function, which means that console.log won't wait until state is really updated.
Your code should be:
handleSubmit(event) {
this.setState({ passedValue: this.state.value },
() => console.log(this.state.passedValue));
event.preventDefault();
}
2) I would move data fetching out of componentWillReceiveProps(), since this lifecycle method will be deprecated from version 17 and it is fired on every render(). Try replacing with componentDidMount() or componentDidUpdate(). Maybe just this small change will solve your problem. If not pls post results and I will take a look again.

ComponentDidMount Updates State, but doesnt seem to re-render component

The problem I face is that it doesn't seem that componentDidMount is re-rendering my component, even though it is updating the state. Lot of code coming up, but it gives context to the issue I'm having. If I need to, I can upload screenshots of what is happening.
Here's the constructor:
export class Upload extends React.Component<RouteComponentProps<{}>, UploadTaggingOptions> {
constructor(props: any) {
super(props);
this.state = {
photographer: [
{ id: null, value: '', label: '' },
],
};
}
Here's component did mount:
componentDidMount {
//Fetch request for photographers from the db
fetch("http://localhost:49775/api/photographers")
.then(res => res.json())
.then((result) => {
var photographerData = this.state!.photographer;
var y = 0;
//Remove the empty object first and foremost. The list should now be totally empty
photographerData.shift();
//The loop to add the galleries to the galleryData array
for (var i in result) {
var id = result[i].id;
var value = result[i].firstname + ' ' + result[i].lastname;
var label = value;
var photographer = { "id": id, "value": value, "label": label };
photographerData.push(photographer);
y++;
}
this.setState({
isLoaded: true,
photographer: photographerData
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
alert("Error loading options for the photographers. Refresh the page. If the error persists, please contact your administrator");
}
)
And finally Render:
public render() {
return <div>
<div className="photographers">
<p><b>Photographer:</b></p>
<DropDown options={this.state!.photographer} />
</div>
}
Just for clarity's sake, there are more components on the screen (hence the extra div for the dropdown component).
I'm not sure why, but the dropdown renders with the blank options I intitialize in the constructor, componentdidupdate does the fetch request AND updates the state to the data that was fetched, but I have to click the blank value in order to load those data values into the dropdown. It is almost like it re-renders after I change the selected value instead of on state change.
I've tried moving those requests into the constructor, but have the same problem. Perhaps
EDIT: Here's the code for the Dropdown component:
import * as React from 'react';
import Select from 'react-select';
const DropDown = (props: any) => {
return (
<div className="dropdown">
<Select
closeOnSelect={!(props.stayOpen)}
disabled={props.disabled}
options={props.options}
placeholder="Select an option..."
removeSelected={props.removeSelected}
simpleValue
value={props.selectedPhotographer}
searchable={true}
multi={true}
/>
</div>
)
}
export default DropDown;
From react official documentation:
Never mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
But in your code you are mutating it, albeit via an assignment to another variable:
var photographerData = this.state!.photographer;
// this DOES mutate the original array.
photographerData.shift();
This can mess with Reacts batching update strategy and can cause delays.
If you do not need the data from original state, you can just do:
var photographerData = [];
window.onload = function() {
console.log('Testing');
let testArr1 = [1, 2, 3]
console.log('TestArr1 length before shift: ' + testArr1.length);
let testArr2 = testArr1;
testArr2.shift();
console.log('TestArr1 length after shift: ' + testArr1.length);
}

How to display submitted form data in same page in Reactjs?

I am new in reactjs. I want to submit form data in same page in reactjs. I am using array to store data but array stores data in one-dimensional and I want it in multi-dimentional form. How can I do that? I am using push() function and there are 5 input field in my form.
Here is my code:
var data = [{}];
export default class Form extends React.Component {
state = {
firstName: "",
lastName: "",
username: "",
email: "",
password: ""
};
change = e => {
this.props.onChange({ [e.target.name]: e.target.value });
this.setState({
[e.target.name]: e.target.value
});
};
onSubmit = e => {
e.preventDefault();
console.log(this.state);
data.push(this.state.firstName,this.state.lastName,this.state.username,this.state.email,this.state.password);
document.getElementById('hello').innerHTML = data;
};
render(){
//form input fields
}
}
Thank You.
Set all your form data values in a state object.
Once, submitted you can set all those values in your state and then you can view the updated state with your form data in the render method.
Something on the lines of
handleSubmit(data) {this.setState({formData: data})}
And then in your render you can view it as
render() { console.log(this.state.formData); return(...); }
Manipulate this variable as you want it now. Care needs to be taken that this state will be null/empty/undefined as per your declaration before the first submit.

React Call Youtube on Exit of Input

Im trying to use youtubes API with the you-tube-api search library, but am having some trouble getting new searches to happen.
I created a new function called search_youtube that I want to be called when a user exits the input. The way I have it setup now, the function is called continuously when I load the html page.
What is the appropriate way to solve this so that when a user exits the input a new search is rendered.
class SearchBar extends Component {
constructor(props) {
super(props)
this.state = {
videos: [],
selectedVideo: null
}
}
searchYoutube(event) {
console.log("called")
YTSearch({ key: API_KEY, term: event.target.value }, (videos) => {
this.setState({
videos: videos,
selectedVideo: videos[0]
})
});
}
render() {
return(
<div className="search-bar">
<input onBlur={this.search_youtube(event)} />
</div>
)
}
}
They are loading everytime you load your page because it's inside your render function, you are calling that function instead of passing the function to onBlur, change that line to this:
<input onBlur={event => this.search_youtube(event)} />
Obs. If you want to learn more about arrow functions check this article.

Resources