rendering a component inside a react page using Menu - reactjs

I have a menu and the main body of the page. I have created different pages as components with some text. All I want is that when I click on the side bar menu, the components are displayed in the main page. How can I make this work?
const items2 = [{
label: 'Rice',
key: 'rice',
},
{
label: 'AB Test',
key: 'ab',
}]
const MainLayout = () => {
const {
token: { colorBgContainer },
} = theme.useToken();
const navigate = useNavigate();
const onClick = (e)=>{navigate(`/${e.key}`)}
return (
<Layout>
<Sider >
<Menu
mode="inline"
items={items2}
onClick = {onClick}
/>
</Sider>
<Content >
//I Want to open the pages here
</Content>
</Layout>
</Content>

To render a component inside other component, React provides a special props name children.
To achieve your requirement, you can do like this:
MainLayout.js:
export const MainLayout = ({children}) => {
const {
token: { colorBgContainer },
} = theme.useToken();
const navigate = useNavigate();
const onClick = (e)=>{navigate(`/${e.key}`)}
return (
<Layout>
<Sider>
<Menu
mode="inline"
items={items2}
onClick={onClick}
/>
</Sider>
<Content>
{children}
</Content>
</Layout>
)
}
In MainLayout.js, you only need to write {children} inside component <Content>, React will do the rest for you to pass the content of Rice or AB or whatever for you. In each page, just place <MainLayout> at the top of the outer of rest of your code.
Please see 2 example files below.
Rice.js:
import MainLayout from './MainLayout';
export const Rice = () => {
// Do some stuffs here...
return (
<MainLayout>
<div>
<h2>Best rated rice in the World</h2>
<ol>
<li>Pinipig</li>
<li>Riz de Camargue</li>
...
</ol>
<div>
</MainLayout>
)
}
Corn.js:
import MainLayout from './MainLayout';
export const Corn = () => {
// Do some stuffs here...
return (
<MainLayout>
<div>
<h2>Best Corn Side Dish Recipes</h2>
<ol>
<li>Whipped-Cream Corn Salad</li>
<li>Classic Grilled Corn on the Cob</li>
...
</ol>
<div>
</MainLayout>
)
}
You can read more and play around with the example code from React's official documents.
It is the basic concept of React, so before you start to build something big, I suggest to follow this docs first or find some series of React tutorials for beginner, they will explain key concepts of React so you would not save more time.

You need to use react-router-dom to navigate when you click other MenuItem. Create your own RouterProvider and put it in the Content.
<Content>
<RouterProvider router={router}>
</Content>
EDIT
Now you have two way to navigate to your Component. First is using Link and set it to your label.
{
label: <Link title="Rice" to="/rice">Rice</Link>,
key: 'rice',
}
Second way is hook useNavigate
const navigate = useNavigate();
const onClick = (e)=>{navigate(`/${e.key}`)}
//Add to Menu
<Menu
onClick={onClick}
//other props
/>

Related

Query a simple button with jest and

I'm trying to test my component, so here it's :
import React from 'react';
import { useNavigate } from 'react-router-dom';
import '../styles/mainPageStyles.css';
const MainPage = (): React.ReactElement => {
const navigate = useNavigate();
const handleBeginQuiz = () => {
navigate('/quiz');
};
return (
<div className="mainPageContainer">
<div className="mainpageWrapper">
<h2 className="defaultFontSize">Welcome to the Trivia Challenge!</h2>
<p className="defaultFontSize">
You will be presented with 10 True or False questions.
</p>
<p className="defaultFontSize">Can you score 100%?</p>
<button
className="beginButton defaultFontSize"
onClick={handleBeginQuiz}
aria-label="BEGIN"
>
BEGIN
</button>
</div>
</div>
);
};
export default MainPage;
as you see it has only one functionality, to redirect me to another page,
I ended to test on click event on the button it self,
It seems like I can't select it, I always get this error:
Main Page Tests › On Begin Button Click
TestingLibraryElementError: Unable to find an element with the text: BEGIN. This could be because the
text is broken up by multiple elements. In this case, you can provide a function for your text matcher to
make your matcher more flexible.
and here are my attempts:
test('On Begin Button Click', () => {
const history = createMemoryHistory();
render(
<MemoryRouter initialEntries={[`/`]}>
<Routes>
<Route element={<MainPage />} />
</Routes>
</MemoryRouter>
);
// I have also tried getByText
const buttonElement = screen.getAllByText('BEGIN', { selector: 'button' });
// fireEvent.click(buttonElement);
// expect(history.location.pathname).toBe('/quiz');
});
try using findByText instead of getByText

Pass component in Next.js getInitialProps to Layout

I'm trying to have a <PageHeader /> component in a <Layout /> which is used on each page. The goal is to use getInitialProps in each page to set the title, subtitle, and actions. The actions are optional and function as page-specific actions such as saving a post.
The layout looks kind of like this:
|----------------------|----------|
| title | actions? |
| subtitle | |
|---------------------------------|
As stated, the <PageHeader /> component is used inside of the <Layout /> component, and I am able to pass basic props using getInitialProps inside of a given page, but passing a component throws an error Error: Objects are not valid as a React child
const Actions = () => {} // menu of button CTA's
const MyPage = () => {} // Page
const MyPage.getInitialProps = (ctx) => {
return {
title: 'Title',
subtitle: 'subtitle',
actions: <Actions />
}
}
const Layout = ({ pageProps, children }) => {
return (
<div>
<PageHeader {...pageProps} />
{children}
</div>
)
}
const PageHeader = ({ actions, title, subtitle }) => {
return (
<div>
<h3>{title}</h3>
<span>{subtitle}</span>
<div>{actions}</div>
</div>
)
}
It's unclear to me if this is a bad pattern or a dead-end. If Next.js's getInitialProps can't or shouldn't be used this way, is there a better way to achieve a dynamic, page-specific component inside of a layout component?

React Smooth Scroll into specific location in my reusable component?

So I originally created a smooth scroll effect with regular HTML css and js, but I'm trying to convert it into react, but I'm not sure if I am doing it properly.
I have a Navbar component and just set the path
<NavLinks to='/#posts'>Posts</NavLinks>
Then I have my Main Content sections which are reusable components that receive data and display a different design based on what data I pass in.
function InfoSection({
lightBg,
id
}) {
return (
<>
<div
id={id} //Passed in the id value
className={lightBg ? 'home__hero-section' : 'home__hero-section darkBg'}
>
// Rest of my JSX is inside here
</div>
</>
);
}
Then in my Data file I'll pass in the values
export const homeObjThree = {
id: 'posts',
lightBg: true,
}
Then I display my reusable components in my index.js page
function Home() {
return (
<>
<InfoSection {...homeObjThree} />
</>
);
}
Then I import that into my App.js file
function App() {
const [isOpen, setIsOpen] = useState(false);
const toggle = () => {
setIsOpen(!isOpen);
};
return (
<Router>
<Sidebar isOpen={isOpen} toggle={toggle} />
<Navbar toggle={toggle} />
<Home />
<Footer />
</Router>
);
}
So when I inspect, it shows the Home HTML with an id="posts" which I set from my data file, but when I click on the Post nav menu item it doesn't scroll down to the section. Nothing actually happens, so I can't tell if my approach even works for padding in ids into my data file or if I would need to write scroll functions to implement it.

How to dynamically create a React component based on url param?

I'd like my React component to generate based on the url param, in this case, the :id param. I'm struggling rendering the component. I have the different Components defined in separate files (ex. Loader.js, Radio button.js, Accordion menu.js).
Here's my (reduced for clarity) code that is continuously failing :)
import React from 'react';
import { Switch, Link, Route } from 'react-router-dom';
import Grid from '../Components/Grid'
function Overview () {
const components = [
{id: 'accordion-menu',
name: 'Accordion menu'},
{id: 'radio-button',
name: 'Radio button'},
{id: 'loader',
name: 'Loader'},
]
const componentPage = ({match}) => {
const findId = components.find((el) => {
match.params.id = el.id;
return findId.name;
}
)}
return (
<Router>
<div className="components">
<h3>Components</h3>
<p>This header and the menu will always appear on this page!</p>
<menu>
{components.map(({id, name}) => (
<li>
<Link to={`/components/${id}`}>{name}</Link>
</li>
))}
</menu>
<Switch>
<Route exact path={'/components/'} component={Grid}/>
<Route path={'/components/:id'} component={componentPage}/>
</Switch>
</div>
</Router>
)
}
export default Overview;
const componentPage = ({match}) => {
const findId = components.find((el) => {
match.params.id = el.id;
return findId.name;
}
)}
I think what you mean here is to return el.name not findId.name.
the other thing is you are trying to display the function as a component which won't work.

How can I use shared components between routes in Next.js

I'm thinking of migrating from create-react-app to Next.js
I need to share component between routes in Next.js
I tried the Layout file example below and it worked pretty well for me but I have a special case where I need the shared component to be above the router itself.
For example, if I have a video element and want the video to still playing if I changed the route
const Layout = (props) => (
<div>
{props.children}
<video width="400" controls>
<source src="https://www.w3schools.com/html/mov_bbb.mp4" type="video/mp4"/>
Your browser does not support HTML5 video.
</video>
</div>
)
const Home = () => (
<Layout>
<Link href="/about"><a>About</a></Link>
<h1 className='title'>Welcome to Home!</h1>
</Layout>
)
const About = () => (
<Layout>
<Link href="/"><a>Home</a></Link>
<h1 className='title'>Welcome to About!</h1>
</Layout>
)
Example from another project using create-react-app and reach-router
the player component is above the router
<Layout>
<Player/>
<Router className={'router'}>
<Login path={Routes.login}/>
<NotFound404 default/>
<Home path={Routes.root}/>
<Test path={Routes.test}/>
</Router>
<Layout/>
The shared element will be the video tag, the problem is on every route change the video is rendered and replayed
You might wanna check the docs on using pages/_app.js for that.
Example usage is shown below:
import Head from "next/head";
type Props = {
pageProps: any;
Component: React.FC;
};
const App = ({ Component, pageProps }: Props) => {
return (
<>
<Head>
// You can put whatever you want to put in your document head
</Head>
<YourSharedComponent />
<Component {...pageProps} />
<style>{`
// styles...
`}</style>
</>
);
};
App.getInitialProps = async ({ Component }) => {
const pageProps = Component.getInitialProps && await Component.getInitialProps(ctx, loggedUser);
return {
pageProps,
}
};
export default App;

Resources