ReactJS child components import resolution along with their parent component prefix - javascript

Hi folks I'm very curious to find a way for importing parent/child components with the prefix of their parent component using dot notation.
Before taking your much time I would like to show you guys an example from the react-bootstrap components import style.
import Modal from "react-bootstrap";
# usage
function Example() {
return (
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Modal heading</Modal.Title>
</Modal.Header>
<Modal.Body>Woohoo, you're reading this text in a modal!</Modal.Body>
<Modal.Footer></Modal.Footer>
</Modal>
);
}
render(<Example />);
I'm trying to have this kind of a similar approach.
I have a Layout component and within that, I have 3 more child components Header, Body, and Footer.
Right now I'm using them like this
Layout.js
export const Header = ({ children }) => (
<div className="header">{children}</div>
);
export const Body = ({ children }) => <div className="body">{children}</div>;
export const Footer = ({ children }) => (
<div className="footer">{children}</div>
);
export const Layout = ({ children }) => {
return <section>{children}</section>;
};
const _default = {
Header: (props) => <Header {...props} />,
Body: (props) => <Body {...props} />,
Footer: (props) => <Footer {...props} />
};
export default _default;
App.js
import { Layout, Header, Body, Footer } from "./Layout";
import ParentLayout from "./Layout";
export default function App() {
return (
<div className="App">
<Layout>
<Header>Header goes here</Header>
<Body>Body goes here</Body>
<Footer>Footer goes here</Footer>
</Layout>
{/* as you can see from this example i can't use ParentLayout as a componet */}
<Layout>
<ParentLayout.Header>Header goes here</ParentLayout.Header>
<ParentLayout.Body>Body goes here</ParentLayout.Body>
<ParentLayout.Footer>Footer goes here</ParentLayout.Footer>
</Layout>
{/* so i was wondering if it can be done in this way */}
{/*
<ParentLayout>
<ParentLayout.Header>Header goes here</ParentLayout.Header>
<ParentLayout.Body>Body goes here</ParentLayout.Body>
<ParentLayout.Footer>Footer goes here</ParentLayout.Footer>
</ParentLayout>
*/}
</div>
);
}
I have created this playground so you can quickly hit and try.

Related

How to nest Context and MUI's theme provider?

So I currently have a state to toggle dark / light mode on a website with lots of nested components. I have a root App.js:
function App() {
return (
<DarkModeProvider>
<div className="App">
<HomePage />
</div>
</DarkModeProvider>
);
}
The DarkModeProvider is my react context, and in the next component I have a layout where I have my navigation and routing, that is wrapped in the ThemeProvider:
const HomePage = () => {
const { isDarkTheme } = useContext(DarkModeContext);
return (
<ThemeProvider theme={isDarkTheme ? createTheme(darkTheme) :
createTheme(lightTheme)}>
<DrawerProvider>
<Router>
<Box sx={{ display: "flex" }}>
<Box>
<HomePageInner />
</Box>
<Routes>
<Route path="/inventory" element={<Inventory />} />
<Route path="/orders" element={<Orders />} />
<Route path="/vendors" element={<Vendors />} />
</Routes>
</Box>
</Router>
</DrawerProvider>
</ThemeProvider>
);
};
It works fine, however, I'd like to access the theme context in my "app" class that's in the root App component. If I wrap the DarkModeProvider with the ThemeProvider, I don't have access to the state of the dark / light mode, if I wrap the ThemeProvider with the DarkModeProvider, I lose access to the isDarkTheme state from my context.
Is there a better practice to format this? What I really want is to have a css / style sheet in the source folder as the same level as the app component. I'm unsure how to access my theme provider when it's not in my app component. OR how to have my dark mode state accessible while wrapped inside of the theme provider (or vice versa).
For example my App.CSS:
body {
background-color: theme.primary.palette.main;
/* I would like the body to follow my MUI theme. */
}
a {
color: inherit;
text-decoration: none;
}
Dark Mode Provider:
import { createContext, useState } from "react";
const DarkModeContext = createContext();
export const DarkModeProvider = ({ children }) => {
const [isDarkTheme, setIsDarkTheme] = useState(false);
const changeTheme = () => {
setIsDarkTheme(!isDarkTheme);
};
return (
<DarkModeContext.Provider
value={{
isDarkTheme,
changeTheme,
}}
>
{children}
</DarkModeContext.Provider>
);
};
export default DarkModeContext;
You can move the ThemeProvider component inside App.js file and have a state there for isDarkTheme which you can then use both for DarkModeProvider and ThemeProvider
function App() {
const [isDarkTheme, setIsDarkTheme] = useState(false);
const changeTheme = () => {
setIsDarkTheme(!isDarkTheme);
};
return (
<DarkModeProvider value={{ isDarkTheme, changeTheme }}>
<ThemeProvider theme={isDarkTheme ? createTheme(darkTheme) : createTheme(lightTheme)}>
<div className="App">
<HomePage />
</div>
</ThemeProvider>
</DarkModeProvider>
);
}
Dark Mode Provider:
import { createContext } from "react";
const DarkModeContext = createContext();
export const DarkModeProvider = ({ children, value }) => {
return (
<DarkModeContext.Provider value={value}>
{children}
</DarkModeContext.Provider>
);
};
export default DarkModeContext;

Why is MDX with Next.JS missing IDs on my elements?

I'm mapping custom HTML to some markdown elements for my site.
For some reason, in genId, the build fails because props can be empty string/null. However, this only happens in production build and everything works perfectly locally.
I've also noticed that upon refresh, the ids are all missing, whereas on initial page load from a separate page, they are present.
The intention of this code is to take the data off a markdown heading and convert it to a CSS ID.
Components
import postStyling from "../../styles/post/Index.module.css";
const genId = (props) => {
console.log(props);
if (props === undefined || props === "") return "";
return props.toLowerCase().replaceAll(" ", "_");
};
const MDXComponents = {
p: (props) => (
<p {...props} className={postStyling["content"]}>
{props.children}
</p>
),
h1: (props) => (
<h1
{...props}
id={genId(props.children)}
className={`${postStyling["main-heading"]}`}
>
{props.children}
</h1>
),
h2: (props) => (
<h2
{...props}
className={postStyling["post-header"]}
id={genId(props.children)}
>
{props.children}
</h2>
),
h3: (props) => (
<h3
{...props}
className={postStyling["post-header"]}
id={genId(props.children)}
>
{props.children}
</h3>
),
h4: (props) => (
<h4
{...props}
className={postStyling["post-header"]}
id={genId(props.children)}
>
{props.children}
</h4>
),
};
export default MDXComponents;
Refresh Same Page
Clicking from Home Page
Component
import React, { useState, useEffect } from "react";
// Custom Components
import BlogHeader from "./BlogHeader";
import MDXComponents from "../mdx/MDXComponents";
import { MDXRemote } from "next-mdx-remote";
// CSS
import styles from "../../styles/admin/Panel.module.css";
import post from "../../styles/post/Index.module.css";
const PostTitle = ({ title, date }) => {
return (
<div className={post["title"]}>
<span>{title}</span>
<span className={post["date"]}>{date}</span>
</div>
);
};
const TableOfContents = ({ toc }) => {
return (
<div className={post["toc"]}>
<div dangerouslySetInnerHTML={{ __html: toc }}></div>
</div>
);
};
function Post({ content, metadata, toc }) {
let icon = metadata["type"] == "video" ? "🎥" : "📝";
return (
<div className={styles.container}>
<BlogHeader />
<PostTitle title={`${icon} ${metadata.title}`} date={metadata.date} />
<h2 id={post["toc-header"]}>Table of Contents</h2>
<TableOfContents toc={toc} />
<MDXRemote {...content} components={MDXComponents} />
</div>
);
}
export default Post;

Reactstrap Collapse not working within React component

I've been trying to get my React component to work with the Collapse but I cannot seem to get my component to collapse correctly. Right now it will collapse temporarily when the div is clicked, but it will automatically reopen and not actually collapse any of the information needed. This component is taking multiple "modules" and turning them into their own cards. I've tried using a button instead of a div for the "onClick" and have tried with and without the reactstrap Card and CardBody components.
I'm thinking that the useState hook is somehow getting lost with my other props? Any help would be appreciated.
import React, { useState } from "react";
import { Container, Collapse, Card, CardBody } from "reactstrap";
import ReplayCard from "./ReplayCard";
import AttachmentCard from "./AttachmentCard";
const ModuleCard = (props) => {
const module = props.cardID;
const [isOpen, setIsOpen] = useState(false);
const toggle = () => setIsOpen(!isOpen);
return (
<div className="moduleCard">
<div onClick={toggle}>
<h2>{module.m_title}</h2>
<h5>Replay Date: {module.m_date}</h5>
</div>
<Collapse isOpen={isOpen}>
<h5>
{module.m_description}
</h5>
<h3>{module.m_title} Video(s)</h3>
<Container className="ReplayCard" fluid={true}>
{module.m_replay &&
module.m_replay.map((value, index) => {
return (
<ReplayCard
key={index}
cardID={index}
video={value.video}
text={value.text}
/>
);
})}
</Container>
<h3>{module.m_title} Link(s)</h3>
<Container className="AttachmentCard" fluid={true}>
{module.m_attachment &&
module.m_attachment.map((value, index) => {
return (
<AttachmentCard
key={index}
cardID={index}
text={value.text}
link={value.link}
/>
);
})}
</Container>
</Collapse>
</div>
);
};
export default ModuleCard;
The useState does seem to be changing from true to false when a console.log is inserted to the togged but still isn't actually triggering any changes.

Not rendering with react-router-dom

Before I used react-router-dom and I hadn't any problem and I changed my route without any problem.
But now I bring hook inside of my project and I got a problem.
When I use <NavLink>, my route changes but it does not render anything from my component. When I refresh my page, the component will appear.
My App.js:
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
const routes={
route: `/main/symbol/:title/:id`,
exact: true,
component: Symbol,
},
{
route: `/main/symbolDetails/:title/:id`,
exact: true,
component: SymbolDetails,
},
render(){
<Router>
<Switch>
{routes.map((route, k) => (
<Route
key={k}
exact={route.exact}
path={route.route}
component={route.component}
/>
))}
</Switch>
</Router>
}
My Home.js:
(in this component I use navlink for changing my page)
import GridContainer from "../../../components/Grid/GridContainer.js";
import "perfect-scrollbar/css/perfect-scrollbar.css";
// #material-ui/core components
import { makeStyles } from "#material-ui/core/styles";
// core components
import Navbar from "../../../components/Navbars/Navbar.js";
import Sidebar from "../../../components/Sidebar/Sidebar.js";
const useStyles = makeStyles(styles);
export default function Admin({ ...rest }) {
// styles
const classes = useStyles();
const [data, setData] = useState([]);
useEffect(() => getSymbolGroup(), []);
const getSymbolGroup = async () => {
let { data } = await symbolGroup.getSymbolGroup();
setData(data.data);
// console.log("data", data);
};
return (
<div className={classes.wrapper}>
<Sidebar
logoText={"Creative Tim"}
logo={logo}
color={color}
{...rest}
/>
<div className={classes.mainPanel}>
<Navbar
/>
<div className={classes.content}>
<div className={classes.container}>
<GridContainer>
{data &&
data.length &&
data.map((x, key) => {
return (
<div className="Subscrip Bshadow ">
<NavLink
to={`/main/symbol/${x.title}/${x.id}`}
className="a rightanime display awidth flexd"
exact
>
<div className="">
<div className="iconpro display">
<img
className="imgwidth "
src={`http://api.atahlil.com/Core/Download/${x.fileId}`}
/>
</div>
</div>
<div className="">
<p style={{ color: "#a3b0c3", width: "100%" }}>
{x.title}
</p>
</div>
</NavLink>
</div>
);
})}
</GridContainer>
</div>
</div>
)}
I realized my problem.
as I say it was correct when I use in class component.
it is not correct because of my useEffect (hook).
I had to use accolade (I mean {}) after use UseEffect in Home.js component.
home.js
useEffect(() => getSymbolGroup(), []); //it is not correct and I need to refresh my page to render
and the way I had to use useEffect is:
useEffect(() => {
getSymbolGroup();
}, []);
// its correct and does not need to refresh page

How to make React page render only change component instead of the whole page?

So I have a toggle looking like this (see below), but the page always re-render the whole thing on the first time I click on the toggle.
export default function Toggle({isChecked, label}: Props) {
return (
<Wrapper>
<Switch isChecked={isChecked}>
<span />
</Switch>
{label}
</Wrapper>
)
}
Then another component which is using this Toggle component
export default function ToggleBox({isChecked, label, children}: Props) {
return (
<Wrapper>
<Toggle isChecked={isChecked} label={label} />
<Content>{children}</Content>
</Wrapper>
)
}
There is a layout
export default function Layout({...someprops bla bla, children}: Props) {
<Wrapper>
<DesktopImg>
<ImageWrapper>
<Image src={image.url} alt={`${headline} image`} layout="fill" />
</ImageWrapper>
</DesktopImg>
<div>
<Content>
{back && backButton}
<MobileImg>
<Image
src={image.url}
alt={`${headline} image`}
width={image.width}
height={image.height}
/>
</MobileImg>
{headline}
<P gutterSize="medium">{description}</P>
</Content>
<ChildrenContainer>{children}</ChildrenContainer>
</div>
</Wrapper>
}
Then finally the page which use the ToggleBox.
export default function Index({isChecked, label, children}: Props) {
const [check, setCheck] = useState(false)
return (
<Layout>
<div onClick={() => setCheck(!check)}>
<ToggleBox label="some label..." isChecked={check}>
//sometext..
</ToggleBox>
</div>
<Button onClick={nextPage} disabled={!check}>
Next
</Button>
</Layout>
)
}
I kinda tried to use the React.memo method but it doesnt seem to work. Any suggestion to make the page not re-render the whole thing but just the toggle?
Move your state further down the tree, you want it to be as close to the component(s) it impacts as possible, again this will probably require breaking out into smaller components, for example, break out the following into a seperate component -
const NewToggleComponent = () => {
const [check, setCheck] = useState(false)
return (
<div onClick={() => setCheck(!check)}>
<ToggleBox label="some label..." isChecked={check}>
//sometext..
</ToggleBox>
</div>
<Button onClick={nextPage} disabled={!check}>
Next
</Button>
)
}
remove state from the top level component, and use this component in your top level component -
...
<NewToggleComponent />
...

Categories