Cannot manage the fields not filled on Reactjs - javascript

I’m making a dynamic form in reactjs. But I have an annoying problem. I would only like to send the fields of the form that are filled out.
To do this, I use this piece of code that allows me to download all datas that are not filled.
Globally, my code for the sending part is
async handleSubmit(event) {
this.setState({ loading: true });
setTimeout(() => {
this.setState({ loading: false });
}, 2000);
event.preventDefault();
const {
name_contact='', name_contact1='', endfr='', endfr_1='',
} = this.state;
Object.keys(this.state).forEach(key => {
if (!this.state[key]) delete this.state[key];
})
await axios.post(
' MY_endpoint API',
{
name: `${name_contact},${name_contact_1} `,end: `${endfr},${endfr_1});
}
On this piece of code, I delete datas from this.state that are null to remove variables with nothing in them.
But my problem is that I don’t know how to handle the fact that a field is empty in axios.get.
I would like to be able to remove from get.axios the variables already deleted by
Object.keys(this.state).forEach(key => {
if (!this.state[key]) delete this.state[key];
})
For example if I do not fill in the name_contact_1 field, I would like axios.get to be
await axios.post(
' MY_endpoint API',
{
name: `${name_contact}`,end: `${endfr},${endfr_1});
}
or if i'm not puting nothing in endfr
await axios.post(
' MY_endpoint API',
{
name: `${name_contact},${name_contact_1} `,end: `${endfr_1});
}
So my question is : Does anyone have any idea how I can handle this? (It may not be possible with my code structure)
I’m not sure if I’m being clear enough, but if I’m not, tell me and I’ll make sure I change it.
PS: my full code
export default class FormPage extends Component {
constructor(props) {
super(props);
this.state = initialState;
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
showMessage = (bool) => {
setTimeout(() => {
this.setState({
showMessage: true
});
}, 2000);
if (this.state.Showing) return;
this.setState({ Show: true, Showing: true });
setTimeout(() => {
this.setState({ Show: false, Showing: false });
}, 2000);
}
showMessageFalse = (bool) => {
this.setState({
showMessage: false
});
this.setState(initialState);
}
handleChange(event) {
const InputValue = event.target.value;
const stateField = event.target.name;
this.setState({
[stateField]: InputValue,
});
console.log(this.state);
}
async handleSubmit(event) {
this.setState({ loading: true });
setTimeout(() => {
this.setState({ loading: false });
}, 2000);
event.preventDefault();
const {
name_contact='',
} = this.state;
Object.keys(this.state).forEach(key => {
if (!this.state[key]) delete this.state[key];
})
await axios.post(
' MY_endpoint API',
{
name: `${name_contact}`);
}
render() {
const { loading } = this.state;
return (
<div>
<ExpansionPanel title="Contacts" expandedTitle="Contacts" titleIcon="done_all" ><div>
<Container>
<div id="normal"><label id='title2'>Detail du contact</label></div><br/>
<Row align="center">
<Col id= "color" sm={3}> <label> Name: </label></Col> <Col id= "color" sm={3}><Input placeholder="Nom complet" type="string" name="name_contact" value={this.state.name_contact} onChange={this.handleChange}/><br /> </Col>
</Row>
</Container>
</div>
</ExpansionPanel>
<form onSubmit={this.handleSubmit}>
<br /><br /><div id="deb"><Button type="submit" value="Show" onClick={this.showMessageFalse.bind(null, true)} > Update </Button></div>
</form>
</div>
);
}
}

First of all, you need to understand that when you assign the value const {name_contact=''} = this.state; And then you later do delete this.state['name_contact'], like you've done above, you haven't actually changed the value of the name_contact variable. You've changed the value inside the state, but not the value itself.
Secondly, javascript cannot magically create your name string like that for you, with a comma only if there are 2 values. You'll have to write a function to do that.
Something like var name = [name_contact, name_contact1].filter(v =>v).join(','); The .filter function gets rid of the empty values from the array, and the .join turns it into a string with commas between values
So you'll end up with something more like this:
async handleSubmit(event) {
this.setState({ loading: true });
setTimeout(() => {
this.setState({ loading: false });
}, 2000);
event.preventDefault();
const {
name_contact='', name_contact1='', endfr='', endfr_1='',
} = this.state;
const name = [name_contact, name_contact1].filter(v =>v).join(',');
const end = [name_contact, name_contact1].filter(v =>v).join(',');
await axios.post(
' MY_endpoint API',
{
name, end
})
}

Related

function works only in debbuger React

I'm pretty new with React so I need your help.
I have a component with multiple inputs, so i have to validate them.
Submit button is disabled by default, and only if the inputs are not blank, I make it able to Submit.
If I delete the value inside the input, button should go back to disabled.
My problem is, this function(validate function) works only in debugger when I go step by step.
Can someone help me?
Here are segments of my code that I find useful to understand my problem.
this.state = {
profile: newProfile,
disable: true,
};
let newProfile= {
firstName: "",
lastName: "",
nickname: "",
email: ""
};
validate = () => {
console.log(this.state)
debugger;
if (!this.state.profile.name || !this.state.profile.email) {
return false;
} else {
console.log("Profile name and email NOT BLANK")
console.log(this.state)
return true;
}
};
profileChange= ((target, value) => {
this.setState(prevState => {
let profile= this.state.profile;
profile[target] = value;
return {
profile: profile,
}
})
const isValid = this.validate();
if (isValid) {
console.log("valid inputs");
this.setState({disable: false});
}
else{
console.log("invalid inputs");
this.setState({disable: true});
}
});
setState is an asynchronous function (Why?) and at the point where you are calling this.validate, this.state.profile hasn't been set yet.
But when you are walk through the execution step by step, the state is being updated to the value you want and hence it is working for you.
The solution here is to use the callback function that setState provides to execute validate only after the state is set.
profileChange = (target, value) => {
this.setState(prevState => {
return {
profile: {
...prevState.profile,
[target]: value,
}
};
}, () => {
const isValid = this.validate();
if (isValid) {
console.log("valid inputs");
this.setState({ disable: false });
} else {
console.log("invalid inputs");
this.setState({ disable: true });
}
});
};
Also note that I have used prevState inside the setState instead of this.state, so that the profile state is not actually mutated.
The setState function is asynchronous, which means that wile the state is being updated, other functions could be fired.
What I think is happening in your case, is that the state is being updated, but before that happened, this.validate() is already called.
To fix this, you have to add the code you would like to fire after you updated that state, as callback:
this.setState(prevState => {
let profile= this.state.profile;
profile[target] = value;
return {
profile: profile,
}
}, () => {
const isValid = this.validate();
if (isValid) {
console.log("valid inputs");
this.setState({disable: false});
} else {
console.log("invalid inputs");
this.setState({disable: true});
}
});
you can use "disabled" params on input here's an example
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" disabled={!this.state.value}/>
</form>
);
}
}
and here's a codepen i made for you to test faster ragnar

Two inputs not submitting both values on form React js

When I click submit on the button, only 1 value is submitted to the object instead of both. I have worked out that the state is updated correctly until the submit button is pressed and only 1 value is submitted into the obj.
I have used the functions as below:
onChange = (event) => {
this.setState({ term: {term1: event.target.value }});
}
onChange2 = (event) => {
this.setState({ term: {term2: event.target.value }});
}
onSubmit = (event) => {
event.preventDefault();
let obj = {
result1: this.state.term.term1,
result2: this.state.term.term2,
};
{console.log('obj', obj)}
this.setState({
term: {
term1: '',
term2: ''
},
items: [...this.state.items, obj]
});
}
I have used the render as below:
render() {
return (
<div>
<form onSubmit={this.onSubmit}>
<input value={this.state.term.term1} onChange={this.onChange} />
<input value={this.state.term.term2} onChange={this.onChange2} />
<button>Submit</button>
</form>
<List items={this.state.items} />
</div>
);
}
My state structure is as follows:
this.state = {
term: {
term1: '',
term2: ''
},
items: []
};
Any help would be great! Thank you!
It's because you're mutating the term state. You need to update the state like:
onChange = (event) => {
this.setState({ term: {...this.state.term, term1: event.target.value }});
}
Do the similar approach with onChange2. And then you'll get the merged term: term1 and term2.
You may even use updater callback like this:
onChange = (event) => {
this.setState((prevState) => ({
term: {
...prevState.term,
term1: event.target.value
}
}))
}
You may even just define single handler for both changes as commented by Henok:
onChange = (event, termNum) => {
this.setState((prevState) => ({
term: {
...prevState.term,
[termNum]: event.target.value
}
}))
}
And pass the termNum with responding term1, or term2 in your onChange:
onChange={this.onChange(term1)}
// there are sorts of methods to pass the parameter.
You may check my another post for further details about using parameters.
Try below change
onChange = (event) => {
this.setState(prevState => ({
term: {
...prevState.term,
term1: event.target.value
}
}))
}
onChange2 = (event) => {
this.setState(prevState => ({
term: {
...prevState.term,
term2: event.target.value
}
}))
}
I believe your state is overwriting itself each time your onChange functions are called. If you use the spread operator you'll keep your current values of state without over writing it.
onChange2 = (event) => {
this.setState({
term: {
...this.state.term,
term2: event.target.value
}
})
}
Do the same for the other onChange even you have as well.
Try like this
onSubmit = (value1, value2) = ev => {
ev.preventDefault();
console.log(value1,value2)
}
and call like this
<form onSubmit={this.onSubmit(this.state.term.term1,this.state.term.term2)}>

React Input Field logging empty string as first keystroke

I'm not sure what I'm doing wrong, but I have an input field for entering a search term and trying to filter results based on the search term. The problem is that the first value being passed is an empty string and input is offset by 1 item for each keypress after that. For example, if I type 'sea', it would update the search term to be ' se'. Then, when I try to delete the value, it is offset the other direction, so deleting ' se' ends with 's', which can't be deleted.
(Here's a link to the app in progress: https://vibrant-yonath-715bf2.netlify.com/allpokemon. The full search functionality isn't working quite yet. I'm pretty new at this.)
import React, { Component } from 'react';
import Pokemon from './Pokemon';
class PokemonList extends Component {
constructor(props) {
super(props);
this.state = {
pokemonList: [],
searchTerm: '',
fetched: false,
loading: false
};
this.updateResults = this.updateResults.bind(this);
}
componentWillMount() {
this.setState({
loading: true
});
fetch('https://pokeapi.co/api/v2/pokemon?limit=151')
.then(res => res.json())
.then(response => {
this.setState({
pokemonList: response.results,
loading: true,
fetched: true
});
});
}
handleSearchTermChange = (
event: SyntheticKeyboardEvent & { target: HTMLInputElement }
) => {
this.setState({ searchTerm: event.target.value });
this.updateResults();
};
updateResults() {
const filteredList = this.state.pokemonList.filter(
pokemon =>
pokemon.name.toUpperCase().indexOf(this.state.searchTerm.toUpperCase()) >= 0
);
console.log(this.state.searchTerm);
this.setState({
pokemonList: filteredList
});
}
render() {
const { fetched, loading, pokemonList } = this.state;
let content;
if (fetched) {
content = (
<div className="flex-grid">
{pokemonList.map((pokemon, index) => (
<Pokemon key={pokemon.name} id={index + 1} pokemon={pokemon} />
))}
</div>
);
} else if (loading && !fetched) {
content = <p> Loading ...</p>;
} else {
content = <div />;
}
return (
<div>
<input
onChange={this.handleSearchTermChange}
value={this.state.searchTerm}
type="text"
placeholder="Search"
/>
{content}
</div>
);
}
}
export default PokemonList;
setState is asynchronous, so your this.state.searchTerm is not updated when you call updateResults. You could e.g. filter the array in render instead.
Example
class App extends Component {
state = {
pokemonList: [
{ name: "pikachu" },
{ name: "bulbasaur" },
{ name: "squirtle" }
],
searchTerm: ""
};
changeSearchTerm = event => {
this.setState({ searchTerm: event.target.value });
};
render() {
const { pokemonList, searchTerm } = this.state;
const filteredList = pokemonList.filter(pokemon =>
pokemon.name.toUpperCase().includes(searchTerm.toUpperCase())
);
return (
<div>
<input value={searchTerm} onChange={this.changeSearchTerm} />
{filteredList.map(pokemon => <div>{pokemon.name}</div>)}
</div>
);
}
}
I think the problem is that you call this.updateResults();
and then calling this.setState({ searchTerm: event.target.value }); instead of using the callback function for setState.
For example:
this.setState({ searchTerm: event.target.value }, () => this.updateResults());
Hope I got it right.
Update:
Also I see many problems in your code, for example, why you update the list with a filtered list? you don't need to do that:
this.setState({
pokemonList: filteredList
});
Instead of updating the results in the state, you simply need to render the filtered list... meaning your state stay with the original list, also your filterd value, just in the render you pass the filtered list..

React fetch posting multiple values under same array

I'm trying to post multiple values for tag inside an array of my fetch.
However, it is posting them connected in one array.
I would like it to always post it
how tag currently sent
tag:[{name: [null, null]}]
how tag should be sent
tag:[
{name: "value from field"},
{name: "next value from field"}
]
I also welcome suggestions on a more clean/efficient way to do this form.
here is my code:
class AddNew extends React.Component {
constructor(props) {
super(props);
this.onTitleChange = this.onTitleChange.bind(this);
this.onTagChange = this.onTagChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {
options: []
};
}
componentDidMount() {
fetch(TAG_API, {
})
.then(response => response.json())
.then(json => {
this.setState({ options: json });
});
}
onTitleChange(e) {
this.setState({ [e.target.name]: e.target.value });
console.log("the title has changed" + e);
}
onTagChange(value) {
this.setState({ value: value });
console.log("they look like this" + value);
}
handleSubmit(e, value) {
e.preventDefault();
return (
fetch(CREATE_API, {
method: "POST",
body: JSON.stringify({
title: this.state.itemtitle,
tag: [
{
name: this.state.value.map(e => {
e.name;
})
}
]
})
})
.then(res => res.json())
.catch(err => console.error(err))
);
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<textarea
name="itemtitle"
type="text"
placeholder="Add new..."
onChange={this.onTitleChange}
/>
<button type="submit">Save</button>
</form>
<Select
mode="tags"
name="tagfield"
onChange={this.onTagChange}
>
{this.state.options.map(e => (
<Option value={e.name}> {e.name} ({e.taglevel}) </Option>
))}
</Select>
</div>
);
}
}
In your handleSubmit function, your mapping function over this.state.value is never returning a value (see longer explanation later). Even if it did return, you're only ever going to have an array with a single element with all the name values. Try changing the body composition into:
body: JSON.stringify({
title: this.state.itemtitle,
tag: this.state.value.map(e => ({ name: e.name })),
})
Longer explanation: Arrow functions implicitly return, unless you surround the body in curly brackets, which in that case are interpreted as a function block and you need to use explicit return. By surrounding a literal object in round brackets, you can implicitly return one.
The following two examples are equivalent:
const beep = () => {
return { boop: 'bzzt' };
};
const beep = () => ({ boop: 'bzzt' });

setState(…): Can only update a mounted or mounting component

This is the code. No idea as to why there is a problem.
class TeacherForm extends Component {
constructor({ data }) {
super();
this.isUpdatingForm = !! data;
this.state = Object.assign({ ... });
this.handleSubmit = this.handleSubmit.bind(this);
this.removeTeacher = this.removeTeacher.bind(this);
}
handleChange(value, field) {
this.setState({ shouldUpdate: true, [field]: value });
}
handleSubmit(e) {
e.preventDefault();
const { name, subjects, parttime, timing } = this.state;
if (this.isUpdatingForm) {
return update.call({
_id: this.props.data._id,
transaction: { name, subjects, parttime, timing },
}, () => this.setState({ shouldUpdate: false }));
}
return add.call();
}
removeTeacher() {
return remove.call(this.props.data._id);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
...
</form>
);
}
}
The error gets thrown at the handleSubmit method in the callback of update.call. This normally shows up when I call removeTeacher and a list updates and this component unmounts.
It sounds like the callback () => this.setState({ shouldUpdate: false }) is executed after that the component is unmounted. Is that possible? If so, one way to get around that is to replace this part by
return update.call({
_id: this.props.data._id,
transaction: { name, subjects, parttime, timing },
}, () => { !this.unmounted && this.setState({ shouldUpdate: false }); });
and to add
componentWillUnmount() {
this.unmounted = true;
}

Categories