Passing props between components when one component is not a function?

Viewed 135

I am trying to learn react and chatbots and have been working on a chatbot using the react-chatbot-kit library. I have made a dialog that opens before a user accesses the chatbot and the user can choose to speak to a male persona or a female one.

Here are the two components I am working on:

config.js

import React from "react";

import Man from "./Man.png"
import Woman from "./Woman.png"


const config = {
  

  state: {
    userState: null,
    username: null,
    password: null,
    sessionID: null,
  },

  customComponents: {
    botAvatar: (props) => <div class="react-chatbot-kit-chat-bot-avatar-container">
         <img src={Man}/> </div>
    },
};

export default config;

App.js

import React, {useState, useEffect} from "react";
import { Chatbot } from "react-chatbot-kit";
import MessageParser from "./MessageParser";
import ActionProvider from "./ActionProvider";
import config from "./config";
import Dialog from "../node_modules/@mui/material/Dialog";
import Typography from "../node_modules/@mui/material/Typography";
import Button from "../node_modules/@mui/material/Button";


const App = () => {

  const [isDialogOpen, setIsDialogOpen] = useState(true);
  const [persona, setPersona] = useState('');

  const onClickedPersona = (gender) => {
    setPersona(gender);
    setIsDialogOpen(false);
  }


  return (
    <div className="App">
      {<Dialog className="initial-dialog"
       open={isDialogOpen}
      >
        <Typography class="title-text" >
            Who would you like to speak to?
        </Typography>
        
          <div class="inner-container">
            <Button onClick={() => onClickedPersona('Man')}>
               Man
            </Button>
            <Button onClick={() => onClickedPersona('Woman')}>
               Woman
            </Button>
          </div>
      </Dialog>}
      <header className="app-chatbot-container">
        <Chatbot
          config={config}
          messageParser={MessageParser}
          actionProvider={ActionProvider}
        />
      </header>     
    </div>
    
  );
}

export default App;

I would like my chatbot to display a female or male avatar depending on whether the user has chosen 'man' or 'woman' in the initial dialog, but I have not been able to pass the props, or find another way to do it. I am aware that the props can only be passed from parent to child (so in my case from config.js to App.js), but I think what confuses me is that config does not contain a function with arguments and does not return anything. I have been trying anything I found online and hoped it would do it but nothing has worked. Any guidance would be super appreciated!

1 Answers

It's possible that react-chatbot-kit have more straightforward approach to passing props to custom components. However you can pass props to the Avatar component in two ways.

  1. You can use React Context:
const BotContext = React.createContext(null);

const BotAvatar = () => {
  const { persona } = useContext(BotContext); 

  // ... render as you like
};

const config = {
  // ...
  customComponents: {
    botAvatar: (props) => <BotAvatar {...props} />
  }
}

const App = () => {
  const [persona, setPersona] = useState('');
  // ...
  const context = useMemo(() => ({
    persona
  }), [persona]);
  return (
    // ...
    <header className="app-chatbot-container">
      <BotContext.Provider value={context}>
        <Chatbot
          config={config}
          messageParser={MessageParser}
          actionProvider={ActionProvider}
        />
      </BotContext.Provider>
    </header> 
  )
}
  1. If you plan to use a state manager, like Redux Toolkit, you can store props in it
// redux.js
const initialState = {
  persona: "male"
};

export const personaSelector = (state) => state.chatBot.persona;

const chatBotSlice = createSlice({
  name: "chatBot",
  initialState,
  reducers: {
    setPersona(state, { payload }) {
      state.persona = payload;
    }
  }
});

export const { setPersona } = chatBotSlice.actions;

const chatBot = chatBotSlice.reducer;

const store = configureStore({ reducer: combineReducers({ chatBot }) });

export default store;


// App.js
const BotAvatar = () => {
  const persona = useSelector(personaSelector);
  
  // ... render as you like
};

const config = {
  // ...
  customComponents: {
    botAvatar: (props) => <BotAvatar {...props} />
  }
};

const App = () => {
  const dispatch = useDispatch();

  const handlePersonaBotClick = useCallback(
    (persona) => {
      dispatch(setPersona(persona));
    },
    [dispatch]
  );

  return (
    <div>
      <button onClick={() => handlePersonaBotClick("male")}>Male</button>
      <button onClick={() => handlePersonaBotClick("female")}>Female</button>
      // ...
    </div>
  );
}
export default App;


// index.js
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

root.render(
  <Provider store={store}>
    <App />
  </Provider>
);

You can try this approach yourself on CodeSandbox.

Related