Reputation: 15
i'm making a comments system with nextjs and firebase, but when the page loads / the user types things inside the textbox, the button "clicks" itself and sends the current values of the textboxes to firestore
the firestore is then spammed with things like
h
he
hey
I suspect it's because of useState, but i'm not really sure what I can do
here's the parts of my code which involve the useState:
const submitComment = (doc, name, text) => {
firebase.firestore().collection('blogs').doc(doc).update({
comments: firebase.firestore.FieldValue.arrayUnion(
{
name: name,
date: new Date().toString(),
text: text
}
)
})
}
export default function Feedback(props) {
const [state, setState] = useState({
liked: false,
comment: "",
name: ""
})
const toggleLike = () => setState({ ...state, liked: !state.liked })
const commentHandler = (e) => setState({ ...state, comment: e.target.value })
const nameHandler = (e) => setState({ ...state, name: e.target.value })
return (
<>
<div className={styles.container}>
<table className={styles.input} role="presentation">
<tbody>
<tr>
<td rowSpan="2">
<Textarea
id="text"
status="secondary" width="100%"
placeholder="Comments!"
onChange={commentHandler}
/>
</td>
<td>
<Input
id="name"
className={styles.name}
width="100%" height="100%" status="secondary"
placeholder="Name"
onChange={nameHandler}
/>
</td>
</tr>
<tr>
<td>
<Button width="150%" type="secondary" ghost
onClick={submitComment(props.post, state.comment, state.comment)}
>submit</Button>
</td>
</tr>
</tbody>
</table>
</div>
</>
)
}
Upvotes: 1
Views: 1477
Reputation: 5870
Without diving too deep into context and binding this
in javascript, the problem is in your onClick
handler. There are a few ways to solve it, but the gist is that you need to have a function that calls your update/submit function when handling events on DOM elements.
The idea is that you're binding a function to handle the event, which will then call your function when the event happens.
One way is to create an inline anonymous function:
onClick={() => submitComment(props.post, state.comment, state.comment)}
I personally prefer to keep this logic outside of the component body, so I'd instead move it up near the rest of your business logic:
// ev is the click event
const submitComment = (ev) => {
// You can read from state & props here
firebase.firestore().collection('blogs').doc(props.post).update({
comments: firebase.firestore.FieldValue.arrayUnion({
name: state.name,
date: new Date().toString(),
text: state.comment
})
})
}
// Later...
<button onClick={ submitComment }>Submit</button>
Totally unrelated to the click issue, but also in your code you call:
const toggleLike = () => setState({ ...state, liked: !state.liked })
I just wanted to point out that you can use the current state in your setState
(for things like toggling likes as you are), but you should read from the previous state to ensure that you're not getting a bad value. The reason for this is that state changes are batched, not immediately fired. So you want to make sure you're reading the previous value before it's updated (in case anything else is updating it):
const toggleLike = () => {
setState(prevState => { ...prevState, liked: !prevState.liked })
}
Upvotes: 1
Reputation: 1548
Can you try using an anonymous callback on your button like so:
onClick={() => submitComment(props.post, state.comment, state.comment)}
Upvotes: 0