Foge Inator
Foge Inator

Reputation: 15

NextJS Button keeps clicking itself by React's useState

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

Answers (2)

I&#39;m Joe Too
I&#39;m Joe Too

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

nishkaush
nishkaush

Reputation: 1548

Can you try using an anonymous callback on your button like so:

onClick={() => submitComment(props.post, state.comment, state.comment)}

Upvotes: 0

Related Questions