Two inputs not submitting both values on form React js - javascript

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)}>

Related

How to push event targets to an object in an array in state with onChange and setState()

I'm trying to take an onChange event and push it to an array in state rather than just state. JSX and state below:
state = {
draftboard: []
}
....
{picks.slice(0,2).map((pick) => (
<Form.Control
onChange={this.onChange}
defaultValue={pick}
className='mb-2'
size='sm'
name='name'
label={incrementPicks()}
as="select" >
{picks.map((pick) => (
<option>{pick}</option>
))}
</Form.Control>
))}
With my onChange being:
onChange = (e) => {
this.setState(state => {
[e.target.name]: e.target.value
}
}
Problem is this just sets the state to
state: {
[],
name: 1
}
and what I want is
state: {
[{
name: 1
}]
}
How would I set this up properly? Thank you!
I`m not sure about what you want, I think this can help you.
onChange = e => {
this.setState(
{
...this.state,
draftboard : [
{
...this.state.draftboard[0],
[e.target.name]: e.target.value
}
]
}
)
}
Try this
const onChange = (e) => {
e.persist();
this.setState(prevState => ({
draftboard: [
{
[e.target.name]: e.target.value]
},
...prevState.draftboard
]
}));
};
const value = ev.target.value;
const name = ev.target.name;
this.setState(prevState => ({
...prevState,
draftboard: [
...prevState.draftboard,
{ value, name }
]
}))

Cannot manage the fields not filled on Reactjs

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
})
}

Enzyme simulate not changing textarea

Im tryng a simple test to verify if the user can enter text on a text area, but for some reason the simulate method doesnt work..
CommentBox component
class CommentBox extends Component {
state = { comment: "" };
handleChange = event => {
this.setState({ comment: event.value });
};
handleSubmit = e => {
e.preventDefault();
this.setState({ comment: "" });
};
render() {
return (
<form>
<h4>
Add a CommentBox
<textarea onChange={this.handleChange} value={this.state.comment} />
<div>
<button onSubmit={this.handleSubmit}>Submit Comment</button>
</div>
</h4>
</form>
);
}
}
The test
export default CommentBox;
let wrapped;
beforeEach(() => {
wrapped = mount(<CommentBox />);
});
afterEach(() => {
wrapped.unmount();
});
it('has a text area and a button', () => {
expect(wrapped.find('textarea').length).toEqual(1);
expect(wrapped.find('button').length).toEqual(1);
});
it('has a text area that users can type in' ,() => {
const textarea = wrapped.find('textarea');
textarea.simulate('change', {
target: { ,
value: 'testing' }
});
wrapped.update();
expect(textarea.prop('value')).toEqual('testing');
});
error :
● has a text area that users can type in
expect(received).toEqual(expected) // deep equality
Expected: "testing"
Received: ""
Issue #1
handleChange = event => {
this.setState({ comment: event.value });
};
Actually value is event.target.value. But I believe better make destructuring right in arguments:
handleChange = ({ target: { value }}) => {
this.setState({ comment: value });
};
Issue#2:
after rerendering you have to run .find('textarea') again instead of relying on previous value; also you don't ever need wrapped.update()
So
it('has a text area that users can type in' ,() => {
wrapped.find('textarea').simulate('change', {
target: { value: 'testing' }
});
expect(wrapped.find('textarea').props().value).toEqual('testing');
});
works fine.

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' });

Categories