Getting Started with Kuzzle and ReactJS #
This tutorial will help you get started with Kuzzle V2 (+ Javascript SDK 7) and ReactJS. We will create documents in Kuzzle and subscribe to document notifications to develop a realtime chat.
Requirements #
- Node.js >= 12.0.0 (install here (opens new window))
- Create React App (install here (opens new window))
- Running Kuzzle V2 Stack (instructions here)
Prepare your environment #
Create your React app and install all the dependencies from the command line using npm:
npx create-react-app kuzzle-playground
Install Kuzzle's Javascript SDK:
cd kuzzle-playground
npm install kuzzle-sdk@7
We'll rewrite the src/App.js so you can remove everything inside.
Instantiating Kuzzle SDK #
We have to connect the server so that our client can interact with it. To achieve this, create a src/services/kuzzle.js file to put our kuzzle instance declaration:
import { Kuzzle, WebSocket } from 'kuzzle-sdk';
export default new Kuzzle(new WebSocket('localhost'));
You can now edit the src/App.js file to connect to Kuzzle. To do this, add the following imports at the top of the file:
import React from 'react';
import kuzzle from './services/kuzzle';
import './App.css';
Now, define the App
class. The constructor must add a message
property in the state (we'll use it to store the user input message) a username
property, messages
property (we’ll use it to store all messages) and a validate
property (Value that will change the display):
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
username: "",
message: "",
messages: [],
validate: false,
};
}
Then we will establish the connection to kuzzle and create the index and collection of our chat if they don't exist. Once connection is established, we need to ensure our Kuzzle instance is properly initialized before we allow the user to interact with it. We implement this logic in the the following valid()
method in the App Component:
valid = async () => {
await kuzzle.connect();
if (!await kuzzle.index.exists("chat")) {
await kuzzle.index.create("chat");
await kuzzle.collection.create("chat", "messages");
}
await this.subscribeMessages();
await this.fetchMessages();
this.setState({ validate: true });
}
Get the username #
First of all, we need to know the user's name, this function will return the input allowing the user to enter his or her name. Let's add this one.
renderUsernameInput = () => {
if (this.state.validate === false) {
return (
<div className="wrapper">
<input autoFocus name="username" id="username"
onKeyUp={this.handleInputChange}
type="text"
placeholder="Enter your nickname"
/>
<button onClick={this.valid}>Valid</button>
</div>
);
}
}
As you can see we'll need the handleInputChange()
function to save the username
variable
handleInputChange = event => {
const { value, name } = event.target;
this.setState({
[name]: value
});
}
Display the messages #
Then, create the following functions to fetch and display the messages:
getMessage = document => {
const message = {
_id: document._id,
value: document._source.value,
createdAt: document._source._kuzzle_info.createdAt,
username: document._source.username
};
return message;
}
The fetchMessage()
function will search for the first hundred newest messages and store them in our array. We called it in the valid()
function we created above.
fetchMessages = async () => {
const results = await kuzzle.document.search(
'chat',
'messages',
{
sort: {
'_kuzzle_info.createdAt': 'desc'
}
},
{
size: 100
}
);
let messages = results.hits.map(hit => this.getMessage(hit));
this.setState({ messages })
}
Now, add the following function outside the App component to display the messages:
const Message = function (props) {
const { message, username } = props;
return (
<div className={(message.username === username ? 'fromMe' : 'fromOthers') + ' messages'}>
<span> User: <b>{message.username}</b> (</span>
<span> {new Date(message.createdAt).toLocaleString().split("GMT")[0]} )</span>
<p> {message.value} </p>
</div>
);
}
Send messages #
We need to write a simple method that will create a new message document in Kuzzle.
sendMessage = async () => {
if (this.state.message === '') return;
await kuzzle.document.create(
'chat',
'messages',
{
value: this.state.message,
username: this.state.username
}
);
this.setState({ message: '' })
}
We will receive real-time notifications from Kuzzle each time a message is added to our message collection. We will use those notifications to append the messages to our application state.
Now, we need to subscribe to the collection that contains our messages. So let's create our subscribeMessages()
method. It will call Kuzzle's realtime controller to allow us to receive notifications on message creations:
subscribeMessages = () => {
return (
kuzzle.realtime.subscribe(
'chat',
'messages',
{},
notification => {
if (notification.type !== 'document') return;
if (notification.action !== 'create') return;
this.setState({
messages: [
this.getMessage(notification.result),
...this.state.messages
]
})
}
)
);
}
Add an input field bound to the message property, and a button which calls our sendMessage()
function:
renderMessageInput = () => {
if (this.state.validate === true) {
return (
<div className="wrapper">
<input autoFocus type="text"
id="message"
name="message"
onChange={this.handleInputChange}
placeholder="Enter your message"
value={this.state.message}
/>
<button onClick={() => this.sendMessage()}>Send</button>
</div>
);
}
}
Add the following CSS classes in the src/App.css file:
.wrapper {
display: flex;
align-items: center;
justify-content: center;
padding: 15px;
}
.messages {
padding: 10px;
margin: 10px;
width: 45vw;
border-radius: 10px;
}
.fromMe {
text-align: right;
float: right;
margin-left: 49vw;
background-color: rgb(0, 40, 53, 0.3);
;
}
.fromOthers {
text-align: left;
margin-right: 49vw;
float: left;
color: #EEEFFF;
background-color: rgb(0, 40, 53, 0.9);
;
}
Finally, add the render()
method of our App component:
render() {
return (
<div>
{this.renderUsernameInput()}
{this.renderMessageInput()}
<div>
{this.state.messages.map((message, idx) => {
return (<Message key={idx} message={message} username={this.state.username} />)
})}
</div>
</div>
);
}
The entire file should look like this:
import React from 'react';
import kuzzle from './services/kuzzle';
import './App.css';
const Message = function (props) {
const { message, username } = props;
return (
<div className={(message.username === username ? 'fromMe' : 'fromOthers') + ' messages'}>
<span> User: <b>{message.username}</b> (</span>
<span> {new Date(message.createdAt).toLocaleString().split("GMT")[0]} )</span>
<p> {message.value} </p>
</div>
);
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
username: "",
message: "",
messages: [],
validate: false,
};
}
valid = async () => {
await kuzzle.connect();
if (!await kuzzle.index.exists("chat")) {
await kuzzle.index.create("chat");
await kuzzle.collection.create("chat", "messages");
}
await this.subscribeMessages();
await this.fetchMessages();
this.setState({ validate: true });
}
subscribeMessages = () => {
return (
kuzzle.realtime.subscribe(
'chat',
'messages',
{},
notification => {
if (notification.type !== 'document') return;
if (notification.action !== 'create') return;
this.setState({
messages: [
this.getMessage(notification.result),
...this.state.messages
]
})
}
)
);
}
getMessage = document => {
const message = {
_id: document._id,
value: document._source.value,
createdAt: document._source._kuzzle_info.createdAt,
username: document._source.username
};
return message;
}
fetchMessages = async () => {
const results = await kuzzle.document.search(
'chat',
'messages',
{
sort: {
'_kuzzle_info.createdAt': 'desc'
}
},
{
size: 100
}
);
let messages = results.hits.map(hit => this.getMessage(hit));
this.setState({ messages })
}
handleInputChange = event => {
const { value, name } = event.target;
this.setState({
[name]: value
});
}
renderUsernameInput = () => {
if (this.state.validate === false) {
return (
<div className="wrapper">
<input autoFocus name="username" id="username"
onKeyUp={this.handleInputChange}
type="text"
placeholder="Enter your nickname"
/>
<button onClick={this.valid}>Valid</button>
</div>
);
}
}
sendMessage = async () => {
if (this.state.message === '') return;
await kuzzle.document.create(
'chat',
'messages',
{
value: this.state.message,
username: this.state.username
}
);
this.setState({ message: '' })
}
renderMessageInput = () => {
if (this.state.validate === true) {
return (
<div className="wrapper">
<input autoFocus type="text"
id="message"
name="message"
onChange={this.handleInputChange}
placeholder="Enter your message"
value={this.state.message}
/>
<button onClick={() => this.sendMessage()}>Send</button>
</div>
);
}
}
render() {
return (
<div>
{this.renderUsernameInput()}
{this.renderMessageInput()}
<div>
{this.state.messages.map((message, idx) => {
return (<Message key={idx} message={message} username={this.state.username} />)
})}
</div>
</div>
);
}
}
export default App;
To launch this app, just type the following command:
npm start
You can now send new messages to Kuzzle and receive the notifications of messages creation to update your state and display the new messages.
Going further #
Now that you are more familiar with Kuzzle, dive even deeper to learn how to leverage its full capabilities:
- Discover what this SDK has to offer by browsing other sections of this documentation
- Learn more about Kuzzle realtime engine
- Lean how to use Kuzzle Admin Console (opens new window) to manage your users and data
- Learn how to use Koncorde to create incredibly fine-grained and blazing-fast subscriptions
- Follow our guide to learn how to manage users, and how to set up fine-grained access control