Search code examples
javascriptreactjsobjectjsxtemplate-literals

React is rendering [object object] rather than the JSX - react binding


I've found a lot of questions that are similar to this but none that actually answer my question.

I have an object key called "text" that contains a string value. In the middle of the string I need a button element to render but I am getting [Object, Object] instead. I have tried other ways but they will only print the button as a string and not as an element.

In short, I have a string but need a word in the middle of the string to become a button.. How would you go about it?

import { Container, Button } from "@mui/material";
import { makeStyles } from "@mui/styles";
import { InstructionsCard } from "components/layout/directory";
import {
  RiNumber1 as OneIcon,
  RiNumber2 as TwoIcon,
  RiNumber3 as ThreeIcon,
} from "react-icons/ri";

const instructions = [
  {
    id: 1,
    icon: OneIcon,
    title: "step one",
    text: "Parent/guardian must make the reservation for the minor through our website (Anyone under the age of 18).",
  },
// attempting to render button[enter image description here][1]
  {
    id: 2,
    icon: TwoIcon,
    title: "step two",
    text: `Parent/guardian will have to sign the minors waiver that is sent via email ${(
      <Button>Email</Button>
    )} after the reservation is made.`,
  },
  {
    id: 3,
    icon: ThreeIcon,
    title: "step three",
    text: "Parent/guardian must show proof of idenity at checkout or can submit a photo of their ID to our email. ",
  },
];

function MinorSection() {
  const classes = useStyles();

  return (
    <section className={classes.root}>
      <Container className={classes.container}>
        {instructions.map((_instruction, index) => {
          return <InstructionsCard key={index} item={_instruction} />;
        })}
      </Container>
    </section>
  );
}

// custom styles
const useStyles = makeStyles((theme) => ({
  root: {
    margin: theme.spacing(5, 0, 5, 0),
  },
  container: {
    display: "flex",
    flexDirection: "row",
    justifyContent: "space-evenly",
  },
}));

export default MinorSection;

Output: 1

EDIT: The InstructionsCard component is rendering the text like so

import { makeStyles } from "@mui/styles";
import { Card, CardContent, Typography, Divider } from "@mui/material";

const InstructionsCard = ({ item }) => {
  const classes = useStyles();
  const NumberIcon = item.icon;

  return (
    <Card className={classes.root}>
      <CardContent>
        <NumberIcon className={classes.icon} />
        <Typography variant="h5" component="h6">
          {item.title.toUpperCase()}
        </Typography>
        <Divider className={classes.divider} />
        <Typography
          variant="subtitle1"
          component="p"
          sx={{ mb: 1.5 }}
          color="text.secondary"
        >
          {item.text}
        </Typography>
      </CardContent>
    </Card>
  );
};

const useStyles = makeStyles((theme) => ({
  root: {
    background: theme.palette.primary.main,
    borderRadius: theme.spacing(5),
    padding: theme.spacing(2),
    margin: theme.spacing(5),
    width: "100%",
    textAlign: "center",
    boxShadow: `0px 0px 10px 10px ${theme.palette.offset.main}`,
  },
  icon: {
    background: theme.palette.secondary.dark,
    width: "50px",
    height: "50px",
    padding: "15px",
    borderRadius: theme.spacing(5),
  },
  divider: {
    background: theme.palette.secondary.dark,
    padding: "2px",
    width: "15%",
    margin: theme.spacing(1, "auto", 1, "auto"),
  },
}));

export default InstructionsCard;

Solution

  • You can't put JSX elements within a template literal. Doing so will just attempt to stringify them, resulting in the infamous "[object Object]" (see Object.prototype.toString()).

    Simply change your text property to something React / JSX can render, like an array

    text: [
      "Parent/guardian will have to sign the minors waiver that is sent via email ",
      <Button>Email</Button>,
      " after the reservation is made."
    ]
    

    or a fragment

    text: (
      <>
        Parent/guardian will have to sign the minors waiver that is sent via email
        <Button>Email</Button>
        after the reservation is made.
      </>
    ),
    

    If you want to replace specific words in an otherwise static string literal, I would suggest something like the following...

    const word = "email";
    const text = "some text with the word email in it";
    
    const nodes = text
      .split(new RegExp(`(\\b${word}\\b)`, "i"))
      .map((node, i) => (i % 2 ? <Button>{node}</Button> : node));
    

    where nodes is an array you can render directly in JSX.

    Edit elated-margulis-6rb2y1