how to change the menu depending on the width of the screen? - reactjs

I apologize for my English using a translator.
I use React.js.
I have a component "header" that accepts two other components - and .
import React, { Component } from 'react';
import styles from './Header.module.css';
import MainLogo from '../MainLogo/MainLogo';
import NavMenu from '../NavMenu/NavMenu';
import BurgerButton from '../BurgerMenu/BurgerButton';
import BurgerMenu from '../BurgerMenu/BurgerMenu';class Header extends Component {
render() {
return (
<header className={styles.header}>
<nav className={styles.navBar}>
<MainLogo />
<NavMenu />
</nav>
</header>
);
}
}
export default Header;
I also created a separate component .
I want to get this markup option: if the screen size is <= 420px, the component should be rendered in the header. With a screen width >420px, the component should be displayed.
I understand that the question is very banal, but I have not yet found an elegant solution.
Thank you in advance!
P.S. using an external library is not suitable.

You can use react-media to render components conditionally based on the screen's size.
In your case, it would look something like this (adapt for your specific markup)
<header className={styles.header}>
<nav className={styles.navBar}>
<MainLogo />
<NavMenu />
<Media query={{ maxWidth: 420 }}>
{matches => <MyComponent /> // Component is in the header}
</Media>
</nav>
</header>
<Media query={{ minWidth: 421 }}>
{matches => <MyComponent /> // Component is outside of the header}
</Media>

Related

ReactJS error is "This component must be used inside a <RecoilRoot> component."

But I'm twisting it slightly, where I don't have a layout but rather a per page component that I'd like to add to the header.
I am getting the following error:
Account.jsx looks something like this:
import { useRecoilValue } from "recoil";
import { userProfile } from "../../../recoil";
export default function Account() {
const profile = useRecoilValue(userProfile);
return (
<div className="w-screen h-full ">
<header>
<Navbar profile={dataMe} />
</header>
<main className="h-screen relative">
<div className='h-screen flex bg-gray-bg my-15 static'>
<div className='w-full mt-10 m-auto bg-white rounded-lg border'>
<div>
<div>
<dataMe />
</div>
<DetailAccount />
</div>
</div>
</div>
</main>
</div >
);
};
To use Recoil (A state management library for React) properly You have to add RecoilRoot wrap component(s). As we can read in documentation :
Components that use recoil state need RecoilRoot to appear somewhere
in the parent tree.
A good place to put this is in your root component
Example from official docs
import React from 'react';
import Account from './Account.jsx';
import {
RecoilRoot,
atom,
selector,
useRecoilState,
useRecoilValue,
} from 'recoil';
function App() {
return (
<RecoilRoot>
<ComponentsThatUseRecoilState />
{/* in Your case <Account /> component */}
</RecoilRoot>
);
}
You have to wrap your component(s) inside the RecoilRoot if you want the component to be using the Recoil functionality. As of documentations: https://recoiljs.org/docs/api-reference/core/RecoilRoot/
So you have to wrap your code inside <RecoilRoot> before you can use the useRecoilValue hook.
Example:
import {RecoilRoot} from 'recoil';
function AppRoot() {
return (
<RecoilRoot>
<ComponentThatUsesRecoil />
</RecoilRoot>
);
}
It's recommended to use this on root (App.tsx or what ever your root element is called) so all your other components can use the Recoil hooks as intended without having to wrap your components in a RecoilRoot every single time.

Render a new page on react-routing but it renders it inside the current page

This displays a card component that the user sees if the user clicks the <Link>read</Link> it should re-render a new page.
import React from "react";
import { Button } from "react-bootstrap";
import "./CardComponent.css";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import Content from "./ContentFolder/Content";
function CardComponent(props) {
return (
<Router>
<div class="card">
<div className="uppercard">
<img
className="bookCover"
src={props.img}
alt=""
width="120px"
height="150px"
/>
<h3>{props.title}</h3>
<h6>By{props.author}</h6>
</div>
<div className="lowerCard">{props.points}</div>
<Link to={"/" + props.title + props.author}>Read</Link>
</div>
<Switch>
<Route
exact
path={`/${props.title+props.author}`}
component={Content}
>
<Content title={props.title} author={props.author}
points={props.points}
/>
</Route>
</Switch>
</Router>
);
}
export default CardComponent;
On clicking read I want to render this content component on a different page.
In summary, the goal is to display all the information on a new page when the user clicks on one of the card components.
import React from "react";
import Mynavbar from "../Partials/Mynavbar";
import MyFooter from "../Partials/Footer";
import { Container } from "react-bootstrap";
import "./Content.css";
function Content(props) {
return (
<div>
<Mynavbar />
<Container className="main">
<h4>{props.title}</h4>
<h6>By {props.author}</h6>
<ul>
{props.points.map((point, i) => {
return <li>{point}</li>;
})}
</ul>
</Container>
<MyFooter />
</div>
);
}
export default Content;
Problem: Router inside of CardComponent
The Router needs to exist at the highest level of the App. Everything that is inside of the Router and outside of the Switch will be rendered on every page. So right now your card code will show up even on the Content route. We want the Card and the Content to be separate Routes.
Problem: Ambiguous URL Structure
Do you need for your urls to look like "/${props.title+props.author}"? This is a very bad structure because you cannot possibly work backwards from the URL to the content. What is the content for "/Harry PotterJ.K. Rowling"? Which part is the title and which part is the author? There is no separator so you don't know. You would have to loop through a list of all books, joining their title and author and comparing it to your string.
A typical URL would be based on an id, like "/book/5". I don't see any mention of an id here so we can use the title.
Solution
An app routing might look like this:
function App() {
return (
<Router>
<Switch>
<Route path="/book/:title" component={BookDetails}/>
<Route path="/" component={BookList}/>
</Switch>
</Router>
)
}
Let's get rid of all the routing in CardComponent and make it just show a card for a book with a link to the book details.
function CardComponent(props: Book) {
return (
<div className="card">
<div className="uppercard">
<img
className="bookCover"
src={props.img}
alt=""
width="120px"
height="150px"
/>
<h3>{props.title}</h3>
<h6>By{props.author}</h6>
</div>
<div className="lowerCard">{props.points}</div>
<Link to={"/book/" + props.title}>Read</Link>
</div>
);
}
Our home page might show a list of these cards.
function BookList() {
// get books from somewhere -- a database? a json file?
const books = ???;
return (
<ul className="bookList">
{books.map((book) => (
<CardComponent {...book} key={book.title} />
))}
</ul>
);
}
BookDetails is a separate route, so we need to get the book from the URL.
function BookDetails(props: RouteComponentProps) {
// get the title from the URL
// is automatically encoded and needs to be decoded
const title = decodeURIComponent(props.match.params.title);
// find the book object from your data source
const book = ???
// from a JSON array: BOOKS.find(book => book.title.toLowerCase() === title.toLowerCase() );
// redirect to error page if no matching book
if ( ! book ) {
return <Redirect to="/404" />
}
// can render your Content component, but only after we get the book
return (
<Content {...book} />
)
}

Have a common header layout in nextjs

I have 2 pages user.js and nonuser.js and one component header. user.js and nonuser.js have same functionality with slight changes in UI. Now I want to integrate all this. Like when I visit the page by default table of user.js must be viewed. One click of nonuser.js it should change to the nonuser.js table. And I want header to be same for both, content in textbox should not change when I switch between pages.
I'm new to next.js and react
header.js
import React, { Component } from 'react';
import '../Header/header.css';
import { Menu, Input, Icon } from 'antd';
import Link from 'next/link';
class HeaderComponent extends Component {
render() {
return (
<div className="navbar">
<div className="header">
<div className="col-1">
<div className="menu">
<div>
<Link href="/User"><a>Users</a></Link>
</div>
<div>
<Link href="/nonUser"><a>Non Users</a></Link>
</div>
<Input className="text-box" placeholder="Enter name" prefix={<Icon type="search" ></Icon>}></Input>
</div>
</div>
</div>
</div>
)
}
}
export default HeaderComponent
user.js
class User extends Component {
render() {
return (
<React.Fragment>
<div className="ant-table-row">
<div className="table-head-text">
<span className="text">Users({data.length})</span>
<Pagination defaultCurrent={1} total={100} />
</div>
<Table
rowKey={data._id}
columns={this.columns1}
rowSelection={this.rowSelection}
onExpand={this.onExpand}
dataSource={data} />
</div>
</React.Fragment>
)
}
I didn't add nonuser component, its same as user component
index.js
import Header from '../components/Header/header';
import Layout from '../components/Layout';
function App() {
return (
<Header/>
<div>
</div>
)
}
export default App;
I've done this, On first landing the only header is there and on clicking user link in header, header disappears and only table of user is shown.
EDIT:
I tried this header appears in both and I placed a textbox in header .textbox value clears when I switch between pages.
user.js and nonuser.js
render(){
return(
<Layout>
<div>.....</div>
</Layout>
)
}
Also tried
index.js
render() {
return (
<Layout>
<div>
</div>
</Layout>
)
}
layout.js
const Layout = ({children}) => (
<div>
<Header></Header>
{children}
</div>
);
From what I make of your question, you want to use HeaderComponent as a common header for both pages? Then I'd suggest placing it in your components/Layout file. Next will wrap all pages in the layout component, thus adding your header to all pages.
I'm also wondering why you have an index.js file? Unless it's placed in pages/ folder, it isn't something you normally do in Next. The pages user.js and nonuser.js should also be placed in the pages/ folder. Next will then automatically load the to files and provide them under the routes /user and /nonuser (based on the name of the file). This will also make Next wrap each page in the layout component mentioned above.
I'd suggest looking into NextJS learning guide. It provides a very good introduction to NextJS and will make it a lot easier to use NextJS if you. They have a lesson explaining how to use Shared Components which explains exactly what you seem to be looking for.
Hope this helps a bit.
Edit:
Example using _app.js
The following is an example of how to use a custom layout component in next using _app.js. It's based on Nexts own example.
// components/Layout.js
import React, { Component } from 'react';
import Header from './Header';
class Layout extends Component {
render () {
const { children } = this.props
return (
<div className='layout'>
<Header />
{children}
</div>
);
}
}
// pages/_app.js
import React from 'react';
import App from 'next/app';
import Layout from '../components/Layout';
export default class MyApp extends App {
render () {
const { Component, pageProps } = this.props
return (
<Layout>
<Component {...pageProps} />
</Layout>
)
}
}
To get more information on how to make use of _app.js properly, check out their documentation on custom app.

An alternative to create a layout?

I want to create a layout containing the header and footer, with in middle every other component.
It will look something like this:
<Layout>
<Component1/>
<Component2/>
...
</Layout>
Technically I already achieved the desired outcome by using semantic ui react's Container and my code looks like this:
import React from 'react';
import { Container } from 'semantic-ui-react';
import Footer from './Footer';
import Header from './Head';
export default props => {
return (
<Container style={{width:'100%'}}>
<Header/>
<div style={{marginTop: '1%'}}>
{props.children}
</div>
<Footer />
</Container>
);
};
I would like to achieve the same result, but without using the Container offered by semantic-ui-react.
I tried in fact to remove <Container> but I am getting this error:
Line 9: Parsing error: Adjacent JSX elements must be wrapped in an
enclosing tag. Did you want a JSX fragment <>...?
If I remove completely the <div> tag I am getting another error:
Unexpected token, expected ","
I am not 100% sure but I understood that I should be using a class which expands the react component. I tried this which also failed:
class props extends React.Component {
render(){
return (
<Header/>
{props.children}
<Footer />
);
}
};
export default props;
You can remove <Container/>, but you will still need a parent component to wrap <Header/>, <Footer/>, and your content div, if you wish to keep the same markup. If, for semantic reasons, you don't want to use a div, use <React.Fragment/> as zfrisch mentioned
import React from 'react';
import { Container } from 'semantic-ui-react';
import Footer from './Footer';
import Header from './Head';
export default props => {
return (
<div style={{width:'100%'}}> // OR <React.Fragment/>
<Header/>
<div style={{marginTop: '1%'}}>
{props.children}
</div>
<Footer />
</div>
);
};

Can't include React UI frameworks (Grommet)

I have problems importing UI libraries, I had problem with ant design library so I decided to try different one, now I have problem importing Grommet.
What am I doing wrong? I added dependencies according documentation and added examples included in documentation yet still not working.
I am trying to execute this code from documentation
But it doesn't look the same at all
I work with codesandbox.io, here is link for the code on it
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
import Heading from "grommet/components/Heading";
import Box from "grommet/components/Box";
import Header from "grommet/components/Header";
import Title from "grommet/components/Title";
import Search from "grommet/components/Search";
import Menu from "grommet/components/Menu";
import Anchor from "grommet/components/Anchor";
import "grommet-css";
class HelloWorldApp extends React.Component {
render() {
return (
<div>
<Header>
<Title>Sample Title</Title>
<Box flex={true} justify="end" direction="row" responsive={false}>
<Search
inline={true}
fill={true}
size="medium"
placeHolder="Search"
dropAlign={{right: "right"}}
/>
<Menu dropAlign={{right: "right"}}>
<Anchor href="#" className="active">
First
</Anchor>
<Anchor href="#">Second</Anchor>
<Anchor href="#">Third</Anchor>
</Menu>
</Box>
</Header>
<Box>
<Heading>
Hello, I'm a Grommet Component styled with
grommet-css
</Heading>
</Box>
</div>
);
}
}
var element = document.getElementById("root");
ReactDOM.render(<HelloWorldApp />, element);
So according to your code:
<Menu dropAlign={{right: "right"}}>
was missing the icon attribute, without which the Menu component directly renders the Anchor, the menu-items component.
add import for the icon of your choosing, i.e: Actions ( from the example )
import Actions from 'grommet/components/icons/base/Actions';
add the icon attribute to the Menu component:
<Menu
icon={<Actions />}
dropAlign={{ right: 'right' }}
>
<Anchor href="#" className="active">First</Anchor>
<Anchor href="#">Second</Anchor>
<Anchor href="#">Third</Anchor>
</Menu>
here's a codesandbox.io link which fixes your issue:
https://codesandbox.io/s/237xo7y48p

Resources