The current React tutorial using create-react-app puts in index.js which causes all class initiators to be executed twice. The tutorial doesn't cleanup subscriptions / listeners in its UseEffect calls so you end up with duplicate listeners and every message is shown twice in the demo app. The code below exhibits the problem with StrictMode on, but doesn't with it off.
import React, { useState, useEffect } from 'react';
import PubNub from 'pubnub';
import { PubNubProvider, usePubNub } from 'pubnub-react';
const pubnub = new PubNub({
publishKey: 'pub-c-816a2df2-0f37-44ac-a43e-132a1f7f3672',
subscribeKey: 'sub-c-4cf98958-0607-4897-a543-07f0daa97aa1',
userId: 'f8681796-a1d7-4b3e-823b-67944a5c035d',
// logVerbosity: true,
});
function App() {
return (
<PubNubProvider client={pubnub}>
<Chat />
</PubNubProvider>
);
}
function Chat() {
const pubnub = usePubNub();
const [channels] = useState(['awesome-channel']);
const [messages, addMessage] = useState([]);
const [message, setMessage] = useState('');
const handleMessage = event => {
const message = event.message;
if (typeof message === 'string' || message.hasOwnProperty('text')) {
const text = message.text || message;
addMessage(messages => [...messages, text]);
}
};
const sendMessage = message => {
if (message) {
pubnub
.publish({ channel: channels[0], message })
.then(() => setMessage(''));
}
};
useEffect( () => {
console.log("updating pubnub")
console.log(handleMessage)
pubnub.addListener({ message: handleMessage });
return () => {
console.log("cleanup listener")
pubnub.removeListener(handleMessage)
}
}, [pubnub])
useEffect(() => {
console.log("Updating channels or pubnub")
console.log(channels)
pubnub.unsubscribeAll()
pubnub.subscribe({ channels });
return () => {
console.log("cleanup channels")
pubnub.unsubscribeAll()
}
}, [pubnub, channels]);
return (
<div style={pageStyles}>
<div style={chatStyles}>
<div style={headerStyles}>React Chat Example</div>
<div style={listStyles}>
{messages.map((message, index) => {
return (
<div key={`message-${index}`} style={messageStyles}>
{message}
</div>
);
})}
</div>
<div style={footerStyles}>
<input
type="text"
style={inputStyles}
placeholder="Type your message"
value={message}
onKeyPress={e => {
if (e.key !== 'Enter') return;
sendMessage(message);
}}
onChange={e => setMessage(e.target.value)}
/>
<button
style={buttonStyles}
onClick={e => {
e.preventDefault();
sendMessage(message);
}}
>
Send Message
</button>
</div>
</div>
</div>
);
}
const pageStyles = {
alignItems: 'center',
background: '#282c34',
display: 'flex',
justifyContent: 'center',
minHeight: '100vh',
};
const chatStyles = {
display: 'flex',
flexDirection: 'column',
height: '50vh',
width: '50%',
};
const headerStyles = {
background: '#323742',
color: 'white',
fontSize: '1.4rem',
padding: '10px 15px',
};
const listStyles = {
alignItems: 'flex-start',
backgroundColor: 'white',
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'auto',
padding: '10px',
};
const messageStyles = {
backgroundColor: '#eee',
borderRadius: '5px',
color: '#333',
fontSize: '1.1rem',
margin: '5px',
padding: '8px 15px',
};
const footerStyles = {
display: 'flex',
};
const inputStyles = {
flexGrow: 1,
fontSize: '1.1rem',
padding: '10px 15px',
};
const buttonStyles = {
fontSize: '1.1rem',
padding: '10px 15px',
};
export default App;
Notice I've tried to cleanup the example to fix this, first by ensuring channels aren't double-subscribed and then by splitting the listener part of useEffect into its own call with a cleanup function, but I haven't succeeded. Are there any more detailed docs on removeListener? It doesn't seem to have any effect for me.
The current React tutorial using create-react-app puts in index.js which causes all class initiators to be executed twice. The tutorial doesn't cleanup subscriptions / listeners in its UseEffect calls so you end up with duplicate listeners and every message is shown twice in the demo app. The code below exhibits the problem with StrictMode on, but doesn't with it off.
Notice I've tried to cleanup the example to fix this, first by ensuring channels aren't double-subscribed and then by splitting the listener part of useEffect into its own call with a cleanup function, but I haven't succeeded. Are there any more detailed docs on removeListener? It doesn't seem to have any effect for me.