I'm trying to update a note and have both an input and textarea. I can update the state in my input just fine. However, only one additional character will update in my textarea. I know that it has to do with textarea being a controlled component and taking a value attribute rather than a defaultValue attribute. However, I can't seem to figure out what I'm doing wrong.
export default class UpdateNote extends Component {
constructor(props) {
super(props);
this.state = {
note: {},
};
this.handleChange = this.handleChange.bind(this);
}
handleChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
}
submitHandler = (e) => {
e.preventDefault();
const note = {
title: this.state.title,
textBody: this.state.textBody,
};
const id = this.props.match.params.id;
axios
.put(`${URL}/edit/${id}`, note)
.then(response => {
this.setState({ title: '', textBody: '' })
this.setState({ note: response.data })
})
.catch(error => {
console.log(error)
})
}
render() {
return (
<div className="notes-list">
<h2 className="your-notes">Edit Note:</h2>
<form className="input-form" onSubmit={this.submitHandler} >
<input type="text" defaultValue={this.state.note.title} name="title" onChange={this.handleChange} />
<textarea type="textarea" value={this.state.note.textBody} name="textBody" onChange={this.handleChange} />
<button type="submit" className="submit-button">Update</button>
</form>
</div>
)
}
}
Related
I don't think I missed anything in making the form a controlled component. Why doesn't the state doesn't change as characters are being input?
class AddChores extends Component {
state = {
chore: "",
};
handleChange = (evt) => {
this.setState({ chore: evt.target.value });
};
handleSubmit = (evt) => {
evt.preventDefault();
this.props.addChores(this.state);
this.setState({ chore: "" });
};
render() {
console.log(this.state);
return (
<div>
<form onClick={this.handleSubmit}>
<input
type="text"
placeholder="New Chore"
value={this.state.chore}
onChange={this.handleChange}
/>
<button className="button">ADD CHORE</button>
</form>
</div>
);
}
}[![React dev tool showing no simultaneous update][1]][1]
I made some changes as below and it works fine for me. Hope it helps you too.
class AddChores extends Component {
constructor(props) {
super(props);
this.state = {
chore: ""
};
}
handleChange = (event) => {
this.setState({
chore: event.target.value
});
};
handleSubmit = (evt) => {
evt.preventDefault();
// this.props.addChores(this.state);
this.setState({ chore: "" });
};
componentDidUpdate(){
console.log('the state', this.state.chore)
}
render() {
return (
<div>
<form onClick={this.handleSubmit}>
<input
type="text"
placeholder="New Chore"
value={this.state.chore}
onChange={this.handleChange}
/>
</form>
</div>
);
}
}
not sure why is this happening but try using the second form of setState
this.setState(() => ({ chore: evt.target.value}))
check this https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous
I am trying to make a chat. But when I send a message I need to refresh page to get data on the page. I have to components Forms and SendMsg.
Parent:
...
import client from '../Utils/Contentful';
export default class Forms extends Component {
constructor() {
super()
this.state = {
messages: [],
}
}
componentDidMount(){
client.getEntries({limit:300, order: 'sys.createdAt', content_type:'nameTest'}).then(response => {
this.setState({messages: response.items});
}).catch(e => {
console.log(e);
});
}
render() {
return (
<div className="chat">
<div className="container-xl">
<MessageList messages={this.state.messages}/>
<SendMsg />
</div>
</div>
);
}
}
And child component
...
import client from '../Utils/ContentfulCM';
export default class SendMsg extends Component {
constructor() {
super()
this.state = {
message:'',
userEmail:'ddd#gmail.com',
chatName:'ggg'
}
this.sendMessage = this.sendMessage.bind(this)
this.handleChange = this.handleChange.bind(this)
}
handleChange(e) {
this.setState({
message: e.target.value,
})
}
sendMessage(e) {
e.preventDefault();
const form = e.target;
const data = new FormData(form);
client.getSpace(client.space)
.then((space) => space.getEnvironment('master'))
.then((environment) => environment.createEntry('nameTest', {
fields: {
chatName: {
'en-US': data.get('chatName')
},
//... some data
}
}))
.then((entry) => entry.publish())
.catch(console.error)
this.setState({
message: ''
})
}
render() {
return (
<div className="send-message">
<Form className="send-msg" onSubmit={this.sendMessage}>
<FormGroup>
<Input type="hidden" name="userEmail" value={this.state.userEmail}/>
</FormGroup>
<FormGroup>
<Input type="hidden" name="chatName" value={this.state.chatName}/>
</FormGroup>
<FormGroup>
<Input
type="text"
name="text"
onChange={this.handleChange}
value={this.state.message}
placeholder="Write your message here"
required />
</FormGroup>
<FormGroup>
<Input type="hidden" name="dateCreated" value={moment().format()} onChange={this.handleChange}/>
</FormGroup>
</Form>
</div>
);
}
}
I try to add props but not sure about right place for them
Any suggestions?
Update 1
Both components have "import client" (they are different because have uniq accessToken), that's why I can't use them in one component.
Update 2
I've change question according to suggestion below, but still need to refresh page in order to get displayed data.
Parent:
export default class Forms extends Component {
constructor() {
super()
this.state = {
messages: [],
}
this.sendMessage = this.sendMessage.bind(this);
}
componentDidMount(){
client1.getEntries({limit:300, order: 'sys.createdAt', content_type:'nameTest'}).then(response => {
this.setState({messages: response.items});
}).catch(e => {
console.log(e);
});
}
sendMessage(data) {
client2.getSpace(client2.space)
.then((space) => space.getEnvironment('master'))
.then((environment) => environment.createEntry('nameTest', {
fields: {
chatName: {
'en-US': data.get('chatName')
... some data
}
}))
.then((entry) => entry.publish())
.catch(console.error)
}
render() {
return (
<div className="chat">
<div className="container-xl">
<MessageList messages={this.state.messages}/>
<SendMsg onSendMessage={this.sendMessage}/>
</div>
</div>
);
}
}
And child component
export default class SendMsg extends Component {
constructor() {
super()
this.state = {
message:'',
userEmail:'ddd#gmail.com',
chatName:'ggg'
}
this.sendMessage = this.sendMessage.bind(this)
this.handleChange = this.handleChange.bind(this)
}
handleChange(e) {
this.setState({
message: e.target.value,
})
}
sendMessage(e) {
e.preventDefault();
const { onSendMessage } = this.props;
const form = e.target;
const data = new FormData(form);
// if send message handler was passed, invoke with form data
onSendMessage && onSendMessage(data);
this.setState({
message: ''
})
}
render() {
return (
<div className="send-message">
<Form className="send-msg" onSubmit={this.sendMessage}>
<FormGroup>
<Input type="hidden" name="userEmail" value={this.state.userEmail}/>
</FormGroup>
<FormGroup>
<Input type="hidden" name="chatName" value={this.state.chatName}/>
</FormGroup>
<FormGroup>
<Input
type="text"
name="text"
onChange={this.handleChange}
value={this.state.message}
placeholder="Write your message here"
required />
</FormGroup>
<FormGroup>
<Input type="hidden" name="dateCreated" value={moment().format()} onChange={this.handleChange}/>
</FormGroup>
</Form>
</div>
);
}
}
Define the callback in the parent. Split out the logic of sending the message data from extracting it from the form in the child. The parent's callback receives the message data and sends it, while the child component's function pulls the form data, formats it, calls the callback passed in props, and clears the input field.
parent
export default class Forms extends Component {
constructor() {
super()
this.state = {
messages: [],
}
this.sendMessage = this.sendMessage.bind(this);
}
componentDidMount(){
client.getEntries({limit:300, order: 'sys.createdAt', content_type:'nameTest'}).then(response => {
this.setState({messages: response.items});
}).catch(e => {
console.log(e);
});
}
sendMessage(data) {
client.getSpace(client.space)
.then((space) => space.getEnvironment('master'))
.then((environment) => environment.createEntry('nameTest', {
fields: {
chatName: {
'en-US': data.get('chatName')
},
//... some data
}
}))
.then((entry) => entry.publish())
.catch(console.error);
}
render() {
return (
<div className="chat">
<div className="container-xl">
<MessageList messages={this.state.messages}/>
<SendMsg onSendMessage={sendMessage} />
</div>
</div>
);
}
}
child
export default class SendMsg extends Component {
constructor() {
super()
this.state = {
message:'',
userEmail:'ddd#gmail.com',
chatName:'ggg'
}
this.sendMessage = this.sendMessage.bind(this)
this.handleChange = this.handleChange.bind(this)
}
handleChange(e) {
this.setState({
message: e.target.value,
})
}
sendMessage(e) {
e.preventDefault();
const { onSendMessage } = this.props;
const form = e.target;
const data = new FormData(form);
// if send message handler was passed, invoke with form data
onSendMessage && onSendMessage(data);
this.setState({
message: ''
});
}
render() {
return (
<div className="send-message">
<Form className="send-msg" onSubmit={this.sendMessage}>
<FormGroup>
<Input type="hidden" name="userEmail" value={this.state.userEmail}/>
</FormGroup>
<FormGroup>
<Input type="hidden" name="chatName" value={this.state.chatName}/>
</FormGroup>
<FormGroup>
<Input
type="text"
name="text"
onChange={this.handleChange}
value={this.state.message}
placeholder="Write your message here"
required />
</FormGroup>
<FormGroup>
<Input type="hidden" name="dateCreated" value={moment().format()} onChange={this.handleChange}/>
</FormGroup>
</Form>
</div>
);
}
}
UPDATE to include syncing capability
Sync API
Using the Sync API with Javascirpt
Updates to parent component:
Add a class instance timer variable to hold interval timer reference
Create functions to handle syncing data calls
Update componentDidMount to sync initial data when the component mounts, and setup data synchronization polling (since this isn't event driven)
Add timer cleanup in componentWillUnmount lifecycle function
Parent
constructor() {
super()
this.state = {
messages: [],
}
this.syncTimer = null;
this.sendMessage = this.sendMessage.bind(this);
}
initialSyncClient = () => client1.sync({
initial: true
limit:100,
order: 'sys.createdAt',
content_type: 'nameTest',
});
syncClient = () => {
const { nextSyncToken } = this.state;
client1.sync({
nextSyncToken
})
.then(this.handleSyncResponse)
.catch(e => {
console.log(e);
});
};
handleSyncResponse = ({ entries, nextSyncToken}) => {
// response shape is a little different,
// response.entries vs. response.items, so need to access correctly
// also need to save nextSyncToken for all subsequent syncs
this.setState({
messages: entries.items,
nextSyncToken,
});
};
componentDidMount(){
// do initial sync
this.initialSyncClient()
.then(this.handleSyncResponse)
.catch(e => {
console.log(e);
});
// setup sync polling, 15 second interval
this.syncTimer = setInterval(syncClient, 15 * 1000);
}
componentWillUnmount() {
// clean up polling timer when component unmounts
clearInterval(this.syncTimer);
}
NOTE: These changes based purely on the contentful documentation, so there may be need of some tweaking to get working as expected, or if you prefer not using arrow functions, etc...
i don't see where you try to add function sendMessage to parent.
You can provide it by props, why not.
in child component, you can do something like this
interface IChildComponentWithSendMessage{
sendMessage
}
export class ChildComponent extends React.Component<IChildComponentWithSendMessage>
and you can provide you messageMethod by props
also you don't have to do
this.sendMessage = this.sendMessage.bind(this)
also i thinkt that could be your problem why you can't provide this method to child/parent component
you can create functions like this:
sendMessage = () => {
}
Every time I click the button a post is being created and displayed
But the input data isn't showing up
import React, { Component } from 'react'
export default class App extends Component {
state = {
dataArr : [],
isClicked: false,
title : '',
img : ''
}
handleSubmit = (e) => {
e.preventDefault()
}
handleChange = (e) => {
[e.target.name] = e.target.value
}
handleClick = () => {
const copyDataArr = Object.assign([], this.state.dataArr)
copyDataArr.push({
title : this.state.title,
img : this.state.img
})
this.setState({
isClicked : true,
dataArr : copyDataArr
})
}
render() {
console.log(this.state.title)
return (
<div>
<form onSubmit={this.handleSubmit}>
<input type="text" name="title" placeholder="Title.." onChange={this.handleChange}/>
<input type="text" name="img" placeholder="Image.." onChange={this.handleChange}/>
<button onClick={this.handleClick}>SUBMIT</button>
</form>
{ this.state.isClicked ?
this.state.dataArr.map(
(post, index) => {
return(
<div class="div">
<h1>title: {post.title}</h1>
<img src={post.img} alt="ohno"/>
</div>
)
}
)
: null }
</div>
)
}
}
Created an empty array on state and copied the array and pushed in the input values. What am I doing wrong?
Need the onChange to work and the input data to display
Trying to learn forms in react. Hope someone can help out
Your handleChange function is not updating state with the data you enter to the inputs. Looks like you might have forgotten to call this.setState(). Try this
:
handleChange = (e) => {
this.setState({
[e.target.name]: e.target.value
})
}
Also here is a sandbox with the working code: https://codesandbox.io/s/distracted-darkness-xp3nu
You have to add a value attribute to the input tag and pass the state value.
<input type="text" name="title" value={this.state.title} placeholder="Title.." onChange={this.handleChange}/>
Your handle change function should be as follows
handleChange = (e) => {
this.setState({[e.target.name] : e.target.value});
}
I am new to react and I can fetch the result from form input fields. Now I need to update those values and submit to the backend. I am struggling to find a way to pass all the input field values at once.
constructor(props) {
super(props);
this.state = {
items: [],
isLoaded: false,
data: this.props.location.data
};
}
render() {
return (
<div>
<h2>Update Your Profile</h2>
{items.map(item => (
<Form key={item.uId} onSubmit={this.handleSubmit}>
<label>User Name</label>
<input type="text" defaultValue={item.userName}></input>
<label>Email address</label>
<input type="email" defaultValue={item.email}></input>
</div>
<button type="submit" >Update</button>
</Form>
))}
</div>
);
}
handleSubmit = (e) => {
e.preventDefault();
axios.put('http://localhost:3000/api/user/' + this.state.data, this.state.items).then(response => {
//
});
};
My API call looks like this:
app.put('/api/user/:userId', (req, res, err) => {
User.update(
{ userName: req.body.userName, email: req.body.email },
{
where: {
userId: req.params.userId
}
}
).then(function (rowsUpdated) {
res.json(rowsUpdated)
}).catch(err);
});
How can I modify this code to set a value for this.state.items with all the updated fields values and submit it?
I'd recommend to create a new component to wrap around the <Form /> and move the submit/change event handling to that component for each item. This would allow you to be able to extract individual email/userName for any given <Form /> to send as a PUT to your API endpoint as well as handle the respective input value changes.
Parent Component:
class Parent extends Component {
constructor() {
super();
this.state = {
name: 'React',
items: [
{ uId: 1, email: 'foo#test.com', userName: 'bar' },
{ uId: 2, email: 'baz#test.com', userName: 'foobar' }
]
};
}
render() {
return (
<div>
{this.state.items.map(item =>
<MyForm key={item.uId} item={item} data={this.props.location.data} />)}
</div>
);
}
}
Child/Form Component:
import React, { Component } from 'react';
class MyForm extends Component {
constructor(props) {
super(props);
this.state = {
email: this.props.item.email,
userName: this.props.item.userName
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
// https://reactjs.org/docs/forms.html#handling-multiple-inputs
handleChange(e) {
const { target} = event;
const value = target.type === 'checkbox' ? target.checked : target.value;
const { name } = target;
this.setState({ [name]: value });
}
handleSubmit(e) {
e.preventDefault();
const { email, userName } = this.state;
const body = { email, userName };
const json = JSON.stringify(body);
console.log(json);
// axios.put('http://localhost:3000/api/user/' + this.props.data, json).then(response => {});
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>User Name</label>
<input type="text" defaultValue={this.state.userName}></input>
<label>Email address</label>
<input type="email" defaultValue={this.state.email}></input>
<button type="submit" >Update</button>
</form>
);
}
}
export default MyForm;
Here is an example in action.
Hopefully that helps!
I'm trying to implement <RecipeForm /> in my <AddRecipe /> component. Later on I would like to reuse the same form for an update action.
The recipe is still not added to the list.
I'm defining handleAddRecipe in my App.js.
passing it to <AddRecipe /> component
from here passing it to <RecipeForm /> component
What do I need to fix in these components?
<AddRecipe /> component:
class AddRecipe extends Component {
render() {
return (
<div>
<h2>Add New Recipe:</h2>
<RecipeForm
handleAddRecipe={this.props.handleAddRecipe}
/>
</div>
)
}
}
export default AddRecipe;
repo: https://github.com/kstulgys/fcc-recipe-box/blob/master/src/components/AddRecipe.js
I guess the trickiest part is <RecipeForm /> component:
export default class RecipeForm extends React.Component {
constructor(props) {
super(props);
this.state = {
url: this.props.url || '',
title: this.props.title || '',
description: this.props.description || '',
error: ''
};
}
onUrlChange = (e) => {
const url = e.target.value;
this.setState(() => ({ url }));
};
onTitleChange = (e) => {
const title = e.target.value;
this.setState(() => ({ title }));
};
onDescriptionChange = (e) => {
const description = e.target.value;
this.setState(() => ({ description }));
};
onSubmit = (e) => {
e.preventDefault();
if (!this.state.url || !this.state.title || !this.state.description) {
this.setState(() => ({ error: 'Please provide description and amount.'}));
} else {
this.setState(() => ({ error: ''}));
this.props.onSubmit({
url: this.state.url,
title: this.state.title,
description: this.state.description
});
}
}
render () {
const submitText = this.state.title ? 'Update' : 'Create' ;
return (
<div>
<form onSubmit={this.onSubmit}>
{this.state.error && <p>{this.state.error}</p>}
<input
type='text'
placeholder='picture url'
autoFocus
value={this.state.url}
onChange={this.onUrlChange}
/>
<input
type='text'
placeholder='title'
autoFocus
value={this.state.title}
onChange={this.onTitleChange}
/>
<input
type='text'
placeholder='description'
autoFocus
value={this.state.description}
onChange={this.onDescriptionChange}
/>
<button>Add Expense</button>
</form>
</div>
)
}
}
In my opinion the onSubmit function is not being invoked.
The button on the form must be type="submit"
You should bind onSubmit function to current scope, in the constructor with this.onSubmit = this.onSubmit.bind(this)