0

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;

1 Answer 1

2

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

Sign up to request clarification or add additional context in comments.

5 Comments

Thank you! If you don't mind, how would you go about this if the string would have come from an axios call instead?
That entirely depends on exactly what data is in the response
@TylerMiller I've added a generic way to replace specific words with JSX elements and return an array for rendering
I greatly appreciate it! You've cleared up all my confusion..
The Fragment solution is the best!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.