Cómo construir un chatbot con React

Mi filosofía es simple. Para ser bueno en algo, debes hacerlo mucho.

No es suficiente hacerlo una vez. Tienes que hacerlo una y otra vez. No acabará nunca. Usé la misma filosofía para mejorar la programación.

Una cosa que he notado en este viaje es que es mucho más divertido construir cosas que son interesantes y que se ven bien. Cosas que puedes mostrarles a tus amigos y de las que estar orgulloso. Algo que le entusiasma comenzar cuando se sienta frente a su teclado.

Por eso construí un chatbot.

Que se transformó en un paquete npm.

Así que construyamos uno juntos. Si desea asumir este desafío por su cuenta, puede ir directamente a la documentación (que en realidad es un chatbot). O, si eres un aprendiz visual, creé un tutorial en YouTube.

De lo contrario, vamos. Asumiré que tiene Node instalado y acceso al comando npx. Si no, ve a buscarlo aquí.

Configuración inicial

// Run these commands from your command line npx create-react-app chatbot cd chatbot yarn add react-chatbot-kit yarn start

Esto debería instalar el paquete npm y abrir el servidor de desarrollo en localhost: 3000.

A continuación, diríjase App.jsy realice estos cambios:

import Chatbot from 'react-chatbot-kit' function App() { return ( ); }

Gran trabajo. Estamos llegando. Debería ver esto en su servidor de desarrollo ahora:

El chatbot necesita tres accesorios que deben incluirse para que funcione. Primero, necesita una configuración que debe incluir una initialMessagespropiedad con objetos de mensaje de chatbot.

En segundo lugar, necesita una MessageParserclase que debe implementar un método de análisis.

En tercer lugar, necesita una ActionProviderclase que implemente las acciones que queremos realizar en función del análisis del mensaje.

Profundizaremos en esto más tarde. Por ahora, vaya aquí para obtener el código repetitivo para comenzar.

  • Pon el MessageParsercódigo en un archivo llamadoMessageParser.js
  • Pon el ActionProvidercódigo en un archivo llamadoActionProvider.js
  • Ponga el código de configuración en un archivo llamado config.js

Cuando haya terminado, vuelva a su App.jsarchivo y agregue este código:

import React from 'react'; import Chatbot from 'react-chatbot-kit' import './App.css'; import ActionProvider from './ActionProvider'; import MessageParser from './MessageParser'; import config from './config'; function App() { return ( ); }

Ahora debería ver esto en localhost: 3000:

Dulce. Ahora tenemos el chatbot renderizado en la pantalla y podemos escribir en el campo de entrada y enviarlo para enviar un mensaje al chat. Pero cuando intentamos eso, no pasa nada.

Entender cómo funciona el chatbot

Aquí tenemos que hacer una parada en boxes y ver cómo interactúa MessageParsery ActionProviderpara que nuestro bot actúe.

Cuando se inicializa el bot, la initialMessagespropiedad de la configuración se coloca en el estado interno del chatbot en una propiedad llamada messages, que se utiliza para mostrar mensajes en la pantalla.

Además, cuando escribimos y presionamos el botón de envío en el campo de chat, nuestro MessageParser(que pasamos como accesorios al chatbot) está llamando a su parsemétodo. Es por eso que este método debe implementarse.

Echemos un vistazo más de cerca al MessageParsercódigo de inicio:

class MessageParser { constructor(actionProvider) { this.actionProvider = actionProvider; } parse(message) { ... parse logic } }

Si miramos de cerca, este método se construye con un actionProvider. Esta es la misma ActionProviderclase que pasamos como accesorios al chatbot. Esto significa que controlamos dos cosas: cómo se analiza el mensaje y qué acción tomar en función de dicho análisis.

Usemos esta información para crear una respuesta simple de chatbot. Primero altere MessageParserasí:

class MessageParser { constructor(actionProvider) { this.actionProvider = actionProvider; } parse(message) { const lowerCaseMessage = message.toLowerCase() if (lowerCaseMessage.includes("hello")) { this.actionProvider.greet() } } } export default MessageParser

Ahora nuestro MessageParserestá recibiendo el mensaje del usuario, comprobando si incluye la palabra "hola". Si lo hace, llama al greetmétodo en actionProvider.

En este momento, esto fallaría porque no hemos implementado el greetmétodo. Hagámoslo a continuación. Dirígete a ActionProvider.js:

class ActionProvider { constructor(createChatBotMessage, setStateFunc) { this.createChatBotMessage = createChatBotMessage; this.setState = setStateFunc; } greet() { const greetingMessage = this.createChatBotMessage("Hi, friend.") this.updateChatbotState(greetingMessage) } updateChatbotState(message) { // NOTE: This function is set in the constructor, and is passed in // from the top level Chatbot component. The setState function here // actually manipulates the top level state of the Chatbot, so it's // important that we make sure that we preserve the previous state. this.setState(prevState => ({ ...prevState, messages: [...prevState.messages, message] })) } } export default ActionProvider

Agradable. Ahora, si escribimos "hola" en el campo de chat, obtenemos esto:

Fantástico. Ahora que podemos controlar el análisis del mensaje y responder con una acción, intentemos hacer algo más complicado. Intentemos crear un bot que le proporcione recursos de aprendizaje para el lenguaje de programación que solicita.

Creando un bot de aprendizaje

Primero, volvamos a nuestro config.jsarchivo y hagamos algunos cambios leves:

import { createChatBotMessage } from 'react-chatbot-kit'; const config = { botName: "LearningBot", initialMessages: [createChatBotMessage("Hi, I'm here to help. What do you want to learn?")], customStyles: { botMessageBox: { backgroundColor: "#376B7E", }, chatButton: { backgroundColor: "#376B7E", }, }, } export default config

Bien, agregamos algunas propiedades aquí y cambiamos nuestro mensaje inicial. Lo más notable es que le hemos dado un nombre al bot y hemos cambiado el color de los componentes messageboxy chatbutton.

Bien. Ahora llegamos a la parte buena.

No solo podemos analizar mensajes y responder al usuario con un mensaje de chatbot, podemos definir componentes React personalizados que queremos representar con el mensaje. Estos componentes pueden ser cualquier cosa que queramos: son simplemente componentes antiguos de React.

Probémoslo creando un componente de opciones que guiará al usuario hacia las posibles opciones.

Primero, definimos el componente de opciones de aprendizaje:

// in src/components/LearningOptions/LearningOptions.jsx import React from "react"; import "./LearningOptions.css"; const LearningOptions = (props) => { const options = [ { text: "Javascript", handler: () => {}, id: 1 }, { text: "Data visualization", handler: () => {}, id: 2 }, { text: "APIs", handler: () => {}, id: 3 }, { text: "Security", handler: () => {}, id: 4 }, { text: "Interview prep", handler: () => {}, id: 5 }, ]; const optionsMarkup = options.map((option) => (  {option.text}  )); return {optionsMarkup} ; }; export default LearningOptions; // in src/components/LearningOptions/LearningOptions.css .learning-options-container { display: flex; align-items: flex-start; flex-wrap: wrap; } .learning-option-button { padding: 0.5rem; border-radius: 25px; background: transparent; border: 1px solid green; margin: 3px; }

Now that we have our component, we need to register it with our chatbot. Head over to config.js and add the following:

import React from "react"; import { createChatBotMessage } from "react-chatbot-kit"; import LearningOptions from "./components/LearningOptions/LearningOptions"; const config = { initialMessages: [ createChatBotMessage("Hi, I'm here to help. What do you want to learn?", { widget: "learningOptions", }), ], ..., widgets: [ { widgetName: "learningOptions", widgetFunc: (props) => , }, ], }

Understanding widgets

Alright. Let's take a breather and explore what we've done.

  1. We created the LearningOptions component.
  2. We registered the component under widgets in our config.
  3. We gave the createChatbotMessage function an options object specifying which widget to render with this message.

The result:

Fantastic, but why did we need to register our component in the config as a widget function?

By giving it a function, we control when we perform the invocation. This allows us room to decorate the widget with important properties inside the chatbot.

The widget that we define will receive a number of properties from the chatbot (some of which can be controlled by config properties):

  1. actionProvider - we give the actionProvider to the widget in order to execute actions if we need to.
  2. setState - we give the top level chatbot setState function to the widget in case we need to manipulate state.
  3. scrollIntoView - utility function to scroll to the bottom of the chat window, should we need to adjust the view.
  4. props - if we define any props in the widget config, those will be passed to the widget under the property name configProps.
  5. state - if we define custom state in the config, we can map it to the widget by using the mapStateToProps property

If you recall, we defined some options in the LearningOptions component:

 const options = [ { text: "Javascript", handler: () => {}, id: 1 }, { text: "Data visualization", handler: () => {}, id: 2 }, { text: "APIs", handler: () => {}, id: 3 }, { text: "Security", handler: () => {}, id: 4 }, { text: "Interview prep", handler: () => {}, id: 5 }, ];

Currently these have an empty handler. What we want to do now is to replace this handler by a call to the actionProvider.

So what do we want to have happen when we execute these functions? Ideally, we'd have some sort of chatbot message, and an accompanying widget that displays a list of links to helpful resources for each topic. So let's see how we can implement that.

First, we need to create the link list component:

// in src/components/LinkList/LinkList.jsx import React from "react"; import "./LinkList.css"; const LinkList = (props) => { const linkMarkup = props.options.map((link) => ( 
  • {link.text}
  • )); return
      {linkMarkup}
    ; }; export default LinkList; // in src/components/LinkList/LinkList.css .link-list { padding: 0; } .link-list-item { text-align: left; font-size: 0.9rem; } .link-list-item-url { text-decoration: none; margin: 6px; display: block; color: #1d1d1d; background-color: #f1f1f1; padding: 8px; border-radius: 3px; box-shadow: 2px 2px 4px rgba(150, 149, 149, 0.4); }

    Great. We now have a component that can display a list of links. Now we need to register it in in the widget section of the config:

    import React from "react"; import { createChatBotMessage } from "react-chatbot-kit"; import LearningOptions from "./components/LearningOptions/LearningOptions"; import LinkList from "./components/LinkList/LinkList"; const config = { ... widgets: [ { widgetName: "learningOptions", widgetFunc: (props) => , }, { widgetName: "javascriptLinks", widgetFunc: (props) => , }, ], }; export default config; 

    So far so good, but we want to dynamically pass in props to this component so that we can reuse it for the other options as well. This means that we need to add another property to the widget object in the config:

    import React from "react"; import { createChatBotMessage } from "react-chatbot-kit"; import LearningOptions from "./components/LearningOptions/LearningOptions"; import LinkList from "./components/LinkList/LinkList"; const config = { ..., widgets: [ ..., { widgetName: "javascriptLinks", widgetFunc: (props) => , props: { options: [ { text: "Introduction to JS", url: "//www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/basic-javascript/", id: 1, }, { text: "Mozilla JS Guide", url: "//developer.mozilla.org/en-US/docs/Web/JavaScript/Guide", id: 2, }, { text: "Frontend Masters", url: "//frontendmasters.com", id: 3, }, ], }, }, ], }; export default config; 

    Now these props will be passed to the LinkList component as props.

    Now we need to do two more things.

    1. We need to add a method to the actionProvider
    class ActionProvider { constructor(createChatBotMessage, setStateFunc) { this.createChatBotMessage = createChatBotMessage; this.setState = setStateFunc; } handleJavascriptList = () => { const message = this.createChatBotMessage( "Fantastic, I've got the following resources for you on Javascript:", { widget: "javascriptLinks", } ); this.updateChatbotState(message); }; updateChatbotState(message) { // NOTICE: This function is set in the constructor, and is passed in from the top level Chatbot component. The setState function here actually manipulates the top level state of the Chatbot, so it's important that we make sure that we preserve the previous state. this.setState((prevState) => ({ ...prevState, messages: [...prevState.messages, message], })); } } export default ActionProvider; 

    2.  We need to add this method as the handler in the LearningOptions component:

    import React from "react"; import "./LearningOptions.css"; const LearningOptions = (props) => { const options = [ { text: "Javascript", handler: props.actionProvider.handleJavascriptList, id: 1, }, { text: "Data visualization", handler: () => {}, id: 2 }, { text: "APIs", handler: () => {}, id: 3 }, { text: "Security", handler: () => {}, id: 4 }, { text: "Interview prep", handler: () => {}, id: 5 }, ]; const optionsMarkup = options.map((option) => (  {option.text}  )); return {optionsMarkup} ; }; export default LearningOptions; 

    Alright! That was quite a lot of information. But if we now try to click the JavaScript option in the chatbot, we get this result:

    Perfect. But we don't want to stop there, this is a chatbot after all. We want to be able to respond to users who want to use the input field as well. So we need to make a new rule in MessageParser.

    Let's update our MessageParser.js file to look like this:

    class MessageParser { constructor(actionProvider) { this.actionProvider = actionProvider; } parse(message) { const lowerCaseMessage = message.toLowerCase(); if (lowerCaseMessage.includes("hello")) { this.actionProvider.greet(); } if (lowerCaseMessage.includes("javascript")) { this.actionProvider.handleJavascriptList(); } } } export default MessageParser; 

    Now try typing "javascript" into the input field and sending the message. You should get the same list in response from the chatbot.

    So there you have it. We've set up a chatbot that renders a list of possible options and responds to user input.

    For now, we've only set up the bot to handle when someone clicks or types in JavaScript, but you can try to expand the other options on your own. Here's a link to the repository.

    All the code is on GitHub, so feel free to dive into the react-chatbot-kit code or docs.

    Conclusion

    Building things is fun, and a great way to expand your skillset. There are no limits to where you could take this next.

    Perhaps you could make a chatbot that finds the ideal product in webshop based on some simple questions (utilising routing in the app), or maybe you can make one for your company taking care of the most common customer inquiries.

    Feel free to expand, come up with new ideas, and test them out. And if you see something that can be improved, send a pull request.

    If you want to improve as a developer, I encourage you to keep building. It truly is the only path forward. If you enjoyed this article, and would like to know when I post more content, you can follow me on Twitter.