I’m stuck using a jquery emoji plugin on one of my components until I finish with a custom plugin I’m building.
For some reason, when I call the emoji plugin inside of componentDidMount, everything works except the ability to utilize a custom button to show the emoji modal. When I use a custom button, the emoji plugin doesn’t attach the event to the button.
What’s crazy is that I can use the same exact code in useEffect, and it attaches the event listener to the custom button just fine.
I verified that the event listener is not attached by looking in the web console at events attached to the element after the page loaded.
You can easily reproduce this problem by placing this component somewhere in an app (and importing jquery with the emoji-area plugin):
import React, {useEffect} from 'react';
export default function CommentInput(props) {
useEffect(() => {
const id = props.blurtId,
$wysiwyg = $('#' + id).emojiarea({
button: '#emoji-btn' + id
});
$.emojiarea.path = '/js/jquery/emojis/';
$.emojiarea.icons = {
':smile:' : 'smile.png',
':angry:' : 'angry.png',
':flushed:' : 'flushed.png',
':neckbeard:' : 'neckbeard.png',
':laughing:' : 'laughing.png'
};
}, []);
return (
<>
<textarea id={props.blurtId} className='blurt-comment-input' />
<i id={'emoji-btn' + props.blurtId} className='fa fa-smile emoji-btn' />
</>
)
}
Simply change this to a class component, and you’ll see that within componentDidMount, everything works except the custom button. Any idea what could cause this change in behavior??
Here is the react class component version:
import React, {Component} from 'react';
class CommentInput extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
const id = this.props.blurtId,
$wysiwyg = $('#' + id).emojiarea({
button: '#emoji-btn' + id
});
$.emojiarea.path = '/js/jquery/emojis/';
$.emojiarea.icons = {
':smile:' : 'smile.png',
':angry:' : 'angry.png',
':flushed:' : 'flushed.png',
':neckbeard:' : 'neckbeard.png',
':laughing:' : 'laughing.png'
};
};
render() {
return (
<>
<textarea id={this.props.blurtId} className='blurt-comment-input' />
<i id={'emoji-btn' + this.props.blurtId} className='fa fa-smile emoji-btn' />
</>
)
}
}
export default CommentInput;
From the previous question, we found out that componentDidMount doesn't have the same behavior with useEffect hook, because componentDidMount invoked synchronously before the browser paints the screen, while useEffect is invoked asynchronously after the browser has already painted the screen.
componentDidMount fires and sets state immediately (not in an async callback) The state change means render() is called again and returns new JSX which replaces the previous render.
Using componentDidMount in functional components with useEffect. This is how we can perform the equivalent of componentDidMount in functional components using the useEffect Hook: useEffect(() => { // Inside this callback function we perform our side effects. });
componentDidMount() : invoked immediately after a component is mounted (inserted into the DOM tree) componentDidUpdate(prevProps, prevState, snapshot) : is invoked immediately after updating occurs. This method is not called for the initial render.
There is a difference between when componentDidMount
and useEffect
fires.
From the useEffect docs :
Unlike componentDidMount and componentDidUpdate, the function passed to useEffect fires after layout and paint
The big difference between componentDidMount
and useEffect
is that useEffect
is run after every render, not just the first one. Since your render
outputs a new element on each render, after the first render the DOM element you attached the emoji thing to doesn't exist anymore, and a new one with the same ID does.
Options:
componentDidUpdate
to handle the subsequent renders. (You'll still need componentDidMount
for the first one.)If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With