I'm new to react and having some very curious behaviour when sending a post request to firebase, hoping someone can explain.
I have a form with two required text areas, both set respective state keys to their input value onChange.
I then have a button hooked to a POST function using an axios instance.
The request goes through absolutely fine if none, or only one of the text areas has input.
If both text areas have input the request doesn't happen and instead I get a complete re-render/refresh of the page.
I have tried chopping the code up many ways, adding conditionals to the POST function changing/testing the instance to see if I'm missing something, but nothing seems to work. I can't understand what is causing the rerender and stopping the request when the only change is a second input value. Any help appreciated!
Here is my code:
class NewPostForm extends Component {
state = {
title: "",
offer: "",
return: "",
};
postHandler = () => {
const post = {
title: this.state.title,
offer: this.state.offer,
return: this.state.return,
};
instance
.post("/posts.json/", post)
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error);
});
}
};
render() {
return (
<form>
<textarea
key={"in0"}
value={this.state.offer}
onChange={(event) => this.setState({ offer: event.target.value })}
required
/>
<textarea
key={"in1"}
value={this.state.return}
onChange={(event) => this.setState({ return: event.target.value })}
required
/>
<div style={{ display: "flex", alignItems: "center" }}>
<MainButton click={this.postHandler} label={"Post"} />
<MainButton click={this.props.click} label={"Cancel"} />
</div>
</form>)
MainButton def:
const MainButton = (props) => {
return (
<button onClick={props.click} style={style}> {props.label} </button>
);
};
A button or submit input within a form can trigger the form being "submitted" to a target, which in classic HTML fashion means sending the inputs back to the server, and then loading what the server returns into a new document. If you're doing fetch/axios/XHR/whatever instead on form button clicks, you need to prevent the default behavior through the event object. Change your onClick handler to:
postHandler = e => {
e.preventDefault();
// rest of the code
};
Related
I have a simple React component that I'm working on creating right now. Basically, the user can input an ID and when they submit, it will display some information that is in a container. The code looks like so
export default class IDContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
Id: '',
isSubmitted: false
};
}
handleSubmit = (event) => {
this.setState({
isSubmitted: true
});
};
handleChange = (event) => {
this.setState({
Id: event.target.value
});
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center'
}}
>
<Input type={'text'} placeholder={"Enter Id"} value={this.state.Id} onChange={this.handleChange} />
<Button type={'submit'} > Lookup </Button>
</div>
<div>
{this.state.isSubmitted && <DetailsContainer Id={this.state.Id} />}
</div>
</form>
);
}
}
The details container has already been created and just returns some details about the Id that has been passed in. I can show the details of the first Id that I pass in just fine. However, when I enter in another Id and submit the form, the DetailsContainer is not re-rendering and is still showing the details for the older Id. I tried moving it around and adding some logic (I even put the DetailsContainer in my state to see if I can manipulate it that way), but that doesn't seem to be working. I see that there is a shouldComponentUpdate() method, and that seems to be what I need to use, but the guides I saw all place it inside of the DetailsContainer. Anyway for me to have it in IDContainer, or is there an easier way to re-render the DetailsContainer?
I think part of the issue here is that once isSubmitted is set, every change you make to the input will be applied to this.state.Id and passed into DetailsContainer.
I think you'd be better off having one variable for tracking the input state, and variable one for tracking the Id you want to pass into DetailsContainer.
state = { Id: null, inputId: '' };
handleSubmit = (event) => {
this.setState({
Id: this.state.inputId
});
};
handleChange = (event) => {
this.setState({
inputId: event.target.value
});
};
render() {
return (
...
<Input ... value={this.state.inputId} />
...
{this.state.Id !== null ? <DetailsContainer Id={this.state.Id} /> : null}
);
}
I am using React to build a simple clone of Reddit. Right now I am working on creating a button to sign a user up to the website. The button's onClick event needs to trigger a function which takes the value of an input field and store the value in the state. I'm sure it's very simple to implement but I haven't been able to figure it out.
I've tried reading documentation and Googling around (mostly StackOverflow links) but I haven't found an answer yet.
Here are some snippets of my code:
class App extends Component {
state = {
username: "",
loggedIn: false
}
signUp = () => {
this.setState({ loggedIn: !this.state.loggedIn });
};
handleChange = event => {
this.setState({
username: event.target.value
});
};
// ... things have been excluded ...
render() {
return (
<div className="App">
<div className="Header">
<p className="Logo">Reddit Clone</p>
{this.state.loggedIn ? null : (
<div style={{ float: "right" }}>
<input
type="text"
name="Username"
onChange={this.handleChange}
></input>
<button onClick={this.signUp}>Sign Up</button>
</div>
)}
</div>
<div style={{ margin: "20px" }}>{threads}</div>
{postBox}
</div>
);
}
I expect the answer will look something like what it does now, maybe just one or two lines of code to get a solution. I've seen similar things done in tutorials but none of them are quite what I need. Hopefully someone can help.
Again, I basically want the "Sign Up" button to transmit the adjacent input fields' text value to my state so I can use the info later.
Change the signUp function into this:
signUp = (e) => {
e.preventDefault() //its for preventing browser refreshing the page by default
this.setState({ loggedIn: !this.state.loggedIn,
username:this.state.username
});
};
As mentioned in the previous comment, after the button in your component fires the onClick function you will have both username and loggedIn in the state. for test purposes, add this piece of code and watch the status of state
componentDidUpdate(){
console.log(this.state)
}
Temporray sample sandbox
So, I am using antd to handle a Form component that has an initial state like this:
...constructor
...super
state = {
tags: ["tag"]
}
handleSubmit = e => {
e.preventDefault();
console.log(this.state);
// gives me Object { tags: [] }
}
render() {
return <Form onSubmit={this.handleSubmit} />
}
This is literally it... but I have no idea why I keep getting an empty array in console. Maybe this is impossible to figure out with this much info, but that's why it's driving me insane. I have noticed that if I comment out some code in this class, that it will all of sudden start showing the array values... no idea.
Any help would be appreciated.
When I go into the React Devtools, it shows the "tag" in my tags array. But, when I hit submit, it clears the array. I'm guessing this is tied in somehow, but I'm not sure why the state is clearing just the tags array and not the other state values. Something to do with a deep clone... ?
For setup default values in ant design form and after validate you need to follow the required steps:
Wrap form:
class YouComponent extends Component { //..... }
const WrappedComponent = Form.create({ name: 'you_form' })(YouComponent);
export default WrappedComponent;
To setup default values:
in you case i think you can use the Select component for tags
<Form layout="inline" onSubmit={this.handleSubmit}>
<Form.Item>
{getFieldDecorator("tags", {
rules: [{ required: true, message: "Pleases select tag!" }]
})(
<Select
mode="multiple"
style={{ width: "100%" }}
placeholder="Please select"
// here setup default selected of tags
defaultValue={this.state.tags}
>
{// map here tags <Select.Option>}
</Select>
)}
</Form.Item>
</Form>
Select documentation
To validate and get value from form:
handleSubmit = e => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
console.log("Received values of form: ", values);
}
});
};
The input doesn't change it's text. It comes pre-filled from the database. For example, if it comes with the text: example, if I press for example the s key, it console logs examples but in the DOM in is still example. Here is the handle code:
handleChange = event => {
this.setState({ [event.target.name]: event.target.value });
console.log(event.target.name);
console.log(event.target.value);
};
and the input field:
<input
type="text"
className="form-control"
name="note"
id=" note"
value={this.state.insurance.note}
onChange={this.handleChange}
/>
EDIT (fixed the render problem, but I don't get the data that I want when I submit the form)
Here is the code for my form:
handleSubmit = event => {
event.preventDefault();
var state = this.state;
axios
.put(`${API_URL}/` + state.id + `/update`, {
tip: state.tip,
date_exp: state.date_exp,
date_notif: state.date_notif,
note: state.note
})
.then(response => {
console.log(response.data);
// window.location = "/view";
})
.catch(error => {
console.log(error);
});
}
and my button is a simple submit button:
<button className="btn btn-success" type="submit">Save</button>
imjared's answer is correct: your problem is in the handleChange function, where you wrongly update the state.
That function should be something like the following one:
handleChange = event => {
const insurance = Object.assign({}, this.state.insurance);
insurance[event.target.name] = event.target.value;
this.setState({insurance});
};
In the first line of the function, you create a deep copy of the current object insurance saved in the state. Then, you update the copied object, and finally update the state.
You're changing this.state.note based on the name property of your input so it makes sense that this.state.insurance.note wouldn't see any updates.
If you try console.logging this.state above your <input>, I bet you'll see what you're after.
I have tried to simplify the code down to what is relevant.
class AnnotatedLayout extends React.Component {
state = {
user: '',
enabled: false,
};
render() {
const { user, enabled } = this.state;
const contentStatus = enabled ? 'Disable' : 'Enable';
const textStatus = enabled ? 'enabled' : 'disabled';
return (
...
<Form onSubmit={this.handleSubmit}>
<FormLayout>
<TextField
value={user}
onChange={this.handleChange('user')}
label="Shop Name"
type="user"
helpText={
<span>
Log in with your store username.
</span>
}
/>
<Stack distribution="trailing">
<Button primary submit>
Submit
</Button>
</Stack>
</FormLayout>
</Form>
...
);
}
handleSubmit = () => {
this.setState({
user: this.state.user
});
localStorage.setItem('user', JSON.stringify(this.state.user));
console.log('submission', this.state);
console.log(this.state.user);
};
handleChange = field => {
return value => this.setState({ [field]: value });
};
}
export default AnnotatedLayout;
Essentially, I have a form component to my webpage that, on submitting, is executing this.handleSubmit, and that function is at the bottom.
What my code SHOULD be doing is saving that submitted string to the localStorage with the key 'user', but evidently (you can see below console.log output) that's not happening.
Any idea what's going on?
My website is hosted locally, tunneled to a URL, and used as the base URL for a shopify embedded app, just to give all relevant context.
UPDATE
handleSubmit = () => {
this.setState({
user: this.state.user
},
() => localStorage.setItem('user', "SMH"),
console.log(localStorage.getItem('user'))
);
console.log('submission', this.state);
};
Check this out, after submitting my text form now this is what I get
is localStorage like local or something, to the point where it doesnt save anything outside of a function??
It seems like you handleChange returns a method, which you need to call again to set the user value.
Instead of
<TextField
value={user}
onChange={this.handleChange('user')}
...
Try
<TextField
value={user}
onChange={e => this.handleChange('user')(e)}
...
The value in handleChange should accept e event value, which is the user value to set.
this.setState(
{
user: this.state.user
},
() => localStorage.setItem('user', JSON.stringify(this.state.user))
);