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;
Related
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.
My idea is that <LandingPage> has <Top> and it is altered by user clicking progress.
<Top> has pageState, then it is changed by button clicking in First and Second page
Is it correct practice? and How can I change the pageState of Top by clicking the button in First and Second page ??
These are what I made so far
export default function LandingPage(props) {
return (
<div>
<Header></Header>
<Top></Top>
<Footer></Footer>
</div>
);
}
const Top = (props) =>{
const [pageState, setPageState] = useState([]);
if (pageState == 1){
return (
<Second></Second>
)
}
elif (pageState == 2){
return (
<Third></Third>
)
}
else {
return (
<First></First>
);
}
}
const First = (props) => {
const classes = useStyles();
return (
<Button size="lg" color="success" style={{margin:"50px"}} onClick={()=>***}>
Move to Second page!</Button>
)
}
const Second = (props) => {
return (
<Button size="lg" color="success" style={{margin:"50px"}} onClick={()=>***}>
Move to Third page!!</Button>
);
}
const Third = (props) => {
return (
<div>Third Page</div>
);
}
I think the best practice is to dedicate specific url to each page. So it can be easier to manage.
Below is my suggestion:
LandingPage.jsx
export default function LandingPage(props) {
return (
<div>
<Header></Header>
{props.children}
<Footer></Footer>
</div>
);
}
First.jsx
import {useHistory} from "react-router-dom"
const First = (props) => {
const classes = useStyles();
const history = useHistory();
return (
<LandingPage>
<Button size="lg" color="success" style={{margin:"50px"}} onClick={()=>history.push("/second")}>
Move to Second page!</Button>
</LandingPage>
)
}
App.js
import {
BrowserRouter as Router,
Switch,
Route,
Redirect,
} from "react-router-dom";
import First from "./First"
import Second from "./Second"
function App() {
return (
<Router>
<Switch>
<Route path="/" component={Login} exact></Route>
<Router path="/first" component={First}></Route>
<Router path="/second" component={Second></Route>
</Switch>
</Router>
)
}
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
This is react-window plugin: https://github.com/bvaughn/react-window
I am using this to render simple list of "Rows".
This is Row comp in which I am try to pass function and const idTestProps=''
class Row extends PureComponent {
render() {
const { index, style } = this.props;
let label;
if (itemStatusMap[index] === LOADED) {
label = `Row ${index}`;
} else {
label = "Loading...";
}
return (
<div className="ListItem" style={style}>
{label}
</div>
);
}
}
This is the Container comp which should pass function and one props to the Row comp:
const outerElementType = forwardRef((props, ref) => (
<div ref={ref} onClick={handleClick} {...props} />
));
export default function App() {
return (
<Fragment>
<InfiniteLoader
isItemLoaded={isItemLoaded}
itemCount={1000}
loadMoreItems={loadMoreItems}
>
{({ onItemsRendered, ref }) => (
<List
className="List"
height={150}
itemCount={1000}
itemSize={35}
// This is outerElementType is way to pass some function down to Row
outerElementType={outerElementType}
width={300}
>
{Row}
</List>
)}
</Fragment>
);
I successfully pass 'function' and works but property not.
How to pass props down in same time with function?
This is codesandbox example:
https://codesandbox.io/s/4zqx79nww0
I have never used react-window but maybe you can do something like this:
import React, { forwardRef } from "react";
import ReactDOM from "react-dom";
import { FixedSizeList as List } from "react-window";
import "./styles.css";
const Row = props => ({ index, style }) => (
<div className={index % 2 ? "ListItemOdd" : "ListItemEven"} style={style}>
Row {index} {props.test}
</div>
);
function handleOnWheel({ deltaY }) {
// Your handler goes here ...
console.log("handleOnWheel()", deltaY);
}
const outerElementType = forwardRef((props, ref) => (
<div ref={ref} onWheel={handleOnWheel} {...props} />
));
const Example = () => (
<List
className="List"
height={150}
itemCount={1000}
itemSize={35}
outerElementType={outerElementType}
width={300}
>
{Row({ test: "test" })}
</List>
);
ReactDOM.render(<Example />, document.getElementById("root"));
I've mocked a simple logic for an accordion collapsible panels in ReactJS. I'm trying to allow for multiple collapsible to be open but I'm not able to avoid all the collapsible to open and close at once no matter which collapsible has been clicked. This below is the logic for the accordion to allow only one collapsible at the time.
//Accordion.js
import React, { useState } from "react";
import styled, { css } from "styled-components";
import PropTypes from "prop-types";
import Collapse from "./Collapse";
import Header from "./Header";
const Accordion = ({ list, icon}) => {
const [isActiveIndex, setActiveIndex] = useState(null);
const toggleItem = index => {
setActiveIndex(isActiveIndex === index ? null : index);
};
return (
<Wrapper>
{list.map((item, index) => {
const checkOpen = isActiveIndex === index;
return (
<Container key={index}>
<Header
title={item.title}
icon={icon}
id={index}
onClick={toggleItem}
/>
<Body isOpen={checkOpen}>
<Collapse isOpen={checkOpen}>{item.content}</Collapse>
</Body>
</Container>
);
})}
</Wrapper>
);
};
I've created the whole mock in CodeSandBox here: https://codesandbox.io/s/1r2mvk87q
For the initial accordion I'm using useState and checking for the active index - for the allow multiple I guess I should check the previous state of the clicked item but I'm not able to pass the clicked item as the only target for the state to be checked.
//AccordionMultiple.js
const AccordionM = ({ list, icon }) => {
const [isOpen, setOpen] = useState(false);
const toggleItemM = index => {
setOpen(prevState => !prevState);
};
return (
<Wrapper>
{list.map((item, index) => {
return (
<Container key={index}>
<Header
title={item.title}
icon={icon}
id={index}
onClick={toggleItemM}
/>
<Body isOpen={isOpen}>
<Collapse isOpen={isOpen}>{item.content}</Collapse>
</Body>
</Container>
);
})}
</Wrapper>
);
};
In order to allow for multiple collapsible column, you can make use of an object instead of a single index
const Accordion = ({ list, icon}) => {
const [isActivePanel, setActivePanel] = useState({});
const toggleItem = index => {
setActivePanel(prevState => ({...prevState, [index]: !Boolean(prevState[index])}));
};
return (
<Wrapper>
{list.map((item, index) => {
const checkOpen = isActivePanel[index];
return (
<Container key={index}>
<Header
title={item.title}
icon={icon}
id={index}
onClick={toggleItem}
/>
<Body isOpen={checkOpen}>
<Collapse isOpen={checkOpen}>{item.content}</Collapse>
</Body>
</Container>
);
})}
</Wrapper>
);
};