Rendering a component within another component - reactjs

I am trying to render components inside another component in the following way. Even when I try to render div elements within the component it's not being displayed.
const Sandbox = () => {
return (
<div>
<RenderHtml
Title="This is the title"
Description="This is the description">
<Example message="Hello World" />
<h1>Render somthing here</h1>
</RenderHtml>
</div>
);
};
export default Sandbox;
Below is the code for RenderHtml component
const MyCard = styled(Card)();
const RenderHtml = (props: any) => {
return (
<MyCard>
<div
style={{
display: "flex",
justifyContent: "space-between",
flexDirection: "column",
}}
>
<Typography variant="h1" sx={{ textAlign: "center" }}>
{props.Title}
</Typography>
<Typography variant="subtitle1" sx={{ textAlign: "center" }}>
{props.Description}
</Typography>
</div>
</MyCard>
);
};
export default RenderHtml;
I went through different examples and tutorials couldn't understand how I can render. If someone could help me out with this please.

You have to add {props.children} into the component if you want to render what's inside like this:
for Component1
const Test=(props)=>{
return <>
{props.children}
</>
}
for Component2
const Test2=(props)=>{
return <>
{props.children}
</>
}
for App.js
<Test>
<Test2>
asd
</Test2>
</Test>

When you render components inside another component, these get passed to the outer component as the prop children. For the inner components to have any effect, the outer component needs to do something with the children prop, which your RenderHtml component currently doesn't do.
For example, if you want the children to be rendered after the title but inside the div you can do this:
const RenderHtml = (props: any) => {
return (
<MyCard>
<div
style={{
display: "flex",
justifyContent: "space-between",
flexDirection: "column",
}}
>
<Typography variant="h1" sx={{ textAlign: "center" }}>
{props.Title}
</Typography>
<Typography variant="subtitle1" sx={{ textAlign: "center" }}>
{props.Description}
</Typography>
{props.children}
</div>
</MyCard>
);
};

Related

Warning "Each child in a list should have a unique "key" prop' even with the key present (React + Material UI)

I am trying to fetch data from a JSON file using map function, but I keep getting this error 'Each child in a list should have a unique "key" prop' even though I set the key={facts.id}. Please how can I get rid of this error? Every other thing is working fine.
import React from "react";
import ResponsiveAppBar from "./ResponsiveAppBar";
import Typography from "#mui/material/Typography";
import { styled } from "#mui/material/styles";
import Box from "#mui/material/Box";
import Paper from "#mui/material/Paper";
import Grid from "#mui/material/Grid";
import Facts from "../sources/facts.json";
import Data from "../sources/credits.json";
const Learn = () => {
const Item = styled(Paper)(({ theme }) => ({
backgroundColor: theme.palette.mode === "dark" ? "#1A2027" : "#fff",
...theme.typography.body2,
padding: theme.spacing(2),
textAlign: "center",
color: theme.palette.text.secondary,
}));
return (
<div>
<ResponsiveAppBar />
{Facts &&
Facts.map((fact) => {
return (
<Box sx={{ flexGrow: 1 }} style={{
marginTop:50}}>
<Grid
container
spacing={2}
elevation={9}
justifyContent="center"
style={{ margin: "auto" }}
>
<Grid item xs={8} >
<Item key={fact.id} >
<Typography
variant="h5"
gutterBottom
component="div"
style={{ fontWeight: 600 }}
>
{fact.id}. {fact.title}
</Typography>
<Typography variant="body1" gutterBottom>
{fact.content}
</Typography>
</Item>
</Grid>
</Grid>
<br />
<br />
</Box>
);
})}
</div>
);
};
export default Learn;
You should assign key to the first element you return :
return (
<Box key={fact.id} sx={{ flexGrow: 1 }} style={{
marginTop:50}}>
I got it working by adding an index to the map function parameter, and setting the key to equal the index.
Facts.map((fact, i) => {
return (
<Box sx={{ flexGrow: 1 }} style={{
marginTop:50} key={i}>

Conditionally Rendering Components From An Array of Components

I have 3 cards I want to render on the screen all with a similar layout. What is this pattern called when we have a component as a value?
const steps = [
{
label: "Order details",
component: OrderDateStep,
},
{
label: "Driver details",
component: OrderDriverStep,
},
{
label: "Acknowledgements",
component: OrderAcknowledgementStep,
},
];
Additionally I keep running into an issue when these are conditionally rendered. I want to wait until stripe has initialised before displaying the form. However, I get an error Error: Rendered more hooks than during the previous render.. I know I can just add the different components but that isn't very scalable. Is there another way I can achieve this re-usable pattern without running into this issue with the number of hooks changing? Why does using step[x].component() change the number of hooks where just using the component does not?
{stripe && (
<Elements
stripe={stripe}
options={{
clientSecret: paymentIntent?.client_secret,
}}
>
{steps.map((step, index) => {
return (
<Box
key={step.label}
sx={{
mt: 3,
}}
>
<Box sx={{ my: 2 }}>
<Typography variant="h5">{step.label}</Typography>
</Box>
{step.component()}
</Box>
);
})}
<Box sx={{ display: "flex", justifyContent: "end" }}>
<Button variant="contained" onClick={submitForm}>
Submit
</Button>
</Box>
</Elements>
)}
If you want to make sure something is filled, or rendered, before displaying other data in react, you can just do
{
loadedVariable ?
<div>
......
</div>
:null
}
If your question is not fully answered by the point i get home i'll be happy to help you further.
Add one more conditionally into your render component to make sure that steps had filled:
{stripe && steps.length && (
<Elements
stripe={stripe}
options={{
clientSecret: paymentIntent?.client_secret,
}}
>
{steps.map((step, index) => {
return (
<Box
key={step.label}
sx={{
mt: 3,
}}
>
<Box sx={{ my: 2 }}>
<Typography variant="h5">{step.label}</Typography>
</Box>
{step.component()}
</Box>
);
})}
<Box sx={{ display: "flex", justifyContent: "end" }}>
<Button variant="contained" onClick={submitForm}>
Submit
</Button>
</Box>
</Elements>
)}

How to access values from context in a separate functional component

I'm trying to build a simple light mode/dark mode into my app I saw this example on Material UI for light/dark mode but I'm not sure how I can get access to the value for when the user clicks toggleColorMode in my Header component if it's being set in toggleColorMode function?
I guess my question is how can I get access to the value of light/dark mode of the context in my Header component if it's in a different function?
Here is my code.
import React, { useState, useEffect } from "react";
import MoreVertIcon from "#mui/icons-material/MoreVert";
import DarkModeIcon from "#mui/icons-material/DarkMode";
import LightModeIcon from "#mui/icons-material/LightMode";
import Paper from "#mui/material/Paper";
import { useTheme, ThemeProvider, createTheme } from "#mui/material/styles";
import IconButton from "#mui/material/IconButton";
import Navigation from "../Navigation/Navigation";
const ColorModeContext = React.createContext({ toggleColorMode: () => {} });
export const Header = (props) => {
const { mode } = props;
const theme = useTheme();
const colorMode = React.useContext(ColorModeContext);
console.log("mode is...", mode);
return (
<div className="header-container">
<Paper
elevation={3}
style={{ backgroundColor: "#1F1F1F", padding: "15px" }}
>
<div
className="header-contents"
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}
>
<div
className="logo"
style={{ display: "flex", alignItems: "center" }}
>
<img
src="/images/header-logo.png"
alt="URL Logo Shortener"
width={"50px"}
/>
<h1 style={{ color: "#ea80fc", paddingLeft: "20px" }}>
URL Shortener
</h1>
</div>
<div className="settings">
<IconButton
sx={{ ml: 1 }}
onClick={colorMode.toggleColorMode}
color="inherit"
aria-label="dark/light mode"
>
{theme.palette.mode === "dark" ? (
<DarkModeIcon
style={{
cursor: "pointer",
marginRight: "10px",
}}
/>
) : (
<LightModeIcon
style={{
cursor: "pointer",
marginRight: "10px",
}}
/>
)}
</IconButton>
<IconButton aria-label="settings">
<MoreVertIcon style={{ color: "#fff", cursor: "pointer" }} />
</IconButton>
</div>
</div>
</Paper>
{/* Navigation */}
<Navigation />
</div>
);
};
export default function ToggleColorMode() {
const [mode, setMode] = React.useState("light");
const colorMode = React.useMemo(
() => ({
toggleColorMode: () => {
setMode((prevMode) => (prevMode === "light" ? "dark" : "light"));
},
}),
[]
);
const theme = React.useMemo(
() =>
createTheme({
palette: {
mode,
},
}),
[mode]
);
return (
<ColorModeContext.Provider value={colorMode}>
<ThemeProvider theme={theme}>
<Header mode={mode} />
</ThemeProvider>
</ColorModeContext.Provider>
);
}
Read the documentation: createContext, useContext. You need to render a ContextProvider in your parent (or top-level) component, then you can get the data in any component in the tree like const { theme } = useContext(ColorModeContext);.
You don't need to pass the mode as props, put it as one of the values in the context and access it.
Here's how you would render it in your example:
<ColorModeContext.Provider value={{colorMode, theme}}>
<Header />
</ColorModeContext.Provider>
You can pass an object inside the value in the context provider, in other word you can pass the toggle function inside your value to be consumed in the childern. thus you gain an access to change your mode state.
Note that the way changes are determined can cause some issues when passing objects as value, this might trigger unnecessary rerendering see Caveats for more info. or refer to the useContext docs
<ColorModeContext.Provider
value={{ colorMode: colorMode, toggleColorMode: toggleColorMode }}
>
<ThemeProvider theme={theme}>
<Header />
</ThemeProvider>
</ColorModeContext.Provider>

MaterialUI TypeScript: Is it possible to add multi-level navigation menu to "NavItem"?

How to create multi-level navigation menu with MaterialUI and TypeScript?
I would like to add to the '/questions' the follwing:
2 navigation menu:
/questions/Tags
/questions/Users
like as in the Screenshot
export function NavBar(...) {
return (
<>
<Drawer>
<List>
<NavItem
to="/questions"
primary="questions"
icon={CloudOff}
/>
)}
<NavItem to="/questions" primary="questions" icon={Window} />
</List>
</Drawer>
</>
);
}
Lot of posibility...
The best way it's to prepare an array with the menu information and map on, something like this :
const TestArea = ({ params }) => {
const menu = [{ mainMenu: "tata", subMenu: ["toto", "titi"] }];
return (
<>
<Toolbar
style={{
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
}}
>
{menu.map((item) => {
return (
<>
<Typography variant="overline">{item.mainMenu}</Typography>
{item.subMenu.map((subItem) => {
return (
<Typography variant="caption" style={{ marginLeft: "40%" }}>
{subItem}
</Typography>
);
})}
</>
);
})}
</Toolbar>
</>
);
};
With this base, you can customize with the component of your choose, render element like link with a path to...
Button color="inherit" component={Link} to="/classic">
Yes, I know, it's plain JSX not TSX, it's just for example.
If you need more information, say me !

Can I Change Box background color on click in material ui with typescript?

I want to change the background color of my Box whenever it is clicked.
OOTB i couldn't find something which could help my use case.
SO, I tried using events onClick but couldn't find the right event which could bring info on selected event and allow me to change the styling value.
Use Case -
i am creating multiple box dynamically and at once only one Box could be highlighted
{allSports !== null &&
allSports?.map((sports) => (
<Grid
item
xs={4}
sx={{ mx: "auto", my: 1, minWidth: "80%" }}
onClick={backgroundChange}
>
<Item
// onClick={() => sportChoose(sports)}
>
<Box sx={{ display: "flex", justifyContent: "space-evenly" }}>
<Box>
<img
src={
require(`../../../../../resources/images/sportsIcons/${sports.icon}`)
.default
}
/>
</Box>
<Box sx={{ m: "auto" }}>
<Typography variant="h6">{sports.name}</Typography>
</Box>
</Box>
</Item>
</Grid>
))}
import { FC, ReactElement, useState } from 'react'
import { Box } from '#mui/material'
export const MuiCard: FC = (): ReactElement => {
const [clicked, setClicked] = useState(false)
const toggleClicked = () => setClicked((prev) => !prev)
return (
<Box
component="div"
onClick={toggleClicked}
sx={{ height: 20, backgroundColor: clicked ? 'red' : 'white' }}
/>
)
}

Resources