React component styles depend on its parent - javascript

Sorry if the question is stupid, I'm new to React.
Let's say I have such a component:
import React from 'react';
import Container from '../Miscellaneous/Container/Container'
import MaskedImgBlock from '../MaskedImgBlock/MaskedImgBlock'
import HeaderImg from 'assets/img/header/header-img.jpg'
import styles from './Header.module.scss'
const header = (props) => {
return(
<header className={styles.mainHeader}>
<Container>
<div className={styles.mainHeader_inner}>
... {/* some other code here */}
<MaskedImgBlock
src={HeaderImg}
alt="Team members photo"/>
</div>
</Container>
</header>
)
};
export default header;
And I have in it a reusable MaskedImgBlock component:
import React from 'react';
import styles from './MaskedImgBlock.module.scss'
const maskedImgBlock = ({ src, alt }) => {
return (
<div className={styles.imgBlock}>
<div className={styles.imgBlock_clipped}>
<img className={styles.imgBlock_img}
src={src}
alt={alt} />
</div>
</div>
)};
export default maskedImgBlock;
This MaskedImgBlock component I want to use inside multiple components in my app, and it must keep its structure and most of styles, but some styles of it's inner elements must be changed according to the component's position.
For example, when this component is inside Header component, one of its inner divs must have a green background-color, and if it's inside a footer component, other inner div must be of yellow background color.
How can I achieve this effect in the most nice way?

Similarly to what's done in the Material-UI framework for React, you could introduce a new prop for the MaskedImgBlock component, like in the following example:
const MaskedImgBlock = ({
src,
alt,
classes = {}
}) => {
const {
root = '',
imgWrapper = '',
img = '',
} = classes
return (
<div className={`${styles.imgBlock} ${root}`}
<div className={`${styles.imgBlock_clipped} ${imgWrapper}`>
<img className={`${styles.imgBlock_img} ${img}`}></igm>
</div>
</div>
)
}
Then, assuming you want to style root and img, you could do the following:
const Header = (props) => {
return (
<header>
<Container>
<div>
<MaskedImgBlock
src={HeaderImg}
alt="Team members photo"
classes={{
root: styles.mainHeader_maskedImgBlock_root,
img: styles.mainHeader_maskedImgBlock_img
}}
/>
</div>
</Container>
</header>
)
}
Or, if you just want the default styling, you don't pass any extra props to your component:
const Header = (props) => {
return (
<header>
<Container>
<div>
<MaskedImgBlock
src={HeaderImg}
alt="Team members photo"
/>
</div>
</Container>
</header>
)
}
This way, you only pass the classes that you want and everything else will default to existing styles.

How do you define styles for MaskedImgBlock classes in parent components?
You use css modules in MaskedImgBlock, so its class names will be generated according with your style/css/or*other-loader config.
If you need couple different representation - it better to add some prop to MaskedImgBlock component and path it from parent like
import React from 'react';
import Container from '../Miscellaneous/Container/Container'
import MaskedImgBlock from '../MaskedImgBlock/MaskedImgBlock'
import HeaderImg from 'assets/img/header/header-img.jpg'
import styles from './Header.module.scss'
const header = (props) => {
return(
<header className={styles.mainHeader}>
<Container>
<div className={styles.mainHeader_inner}>
... {/* some other code here */}
<MaskedImgBlock
theme={'green'}
src={HeaderImg}
alt="Team members photo"/>
</div>
</Container>
</header>
)
};
export default header;
If you want to customize your MaskedImgBlock component with many styles from different parent compoments - the best approach is adding className (or etc.) prop to your MaskedImgBlock component, where you can pass class name from parent component (like your Header component).
import React from 'react';
import Container from '../Miscellaneous/Container/Container'
import MaskedImgBlock from '../MaskedImgBlock/MaskedImgBlock'
import HeaderImg from 'assets/img/header/header-img.jpg'
import styles from './Header.module.scss'
const header = (props) => {
return(
<header className={styles.mainHeader}>
<Container>
<div className={styles.mainHeader_inner}>
... {/* some other code here */}
<MaskedImgBlock
className={styles.maskedImg}
src={HeaderImg}
alt="Team members photo"/>
</div>
</Container>
</header>
)
};
export default header;
And new MaskedImgBlock
import React from 'react';
import styles from './MaskedImgBlock.module.scss'
const maskedImgBlock = ({ src, alt, className = '' }) => {
return (
<div className={`${styles.imgBlock} ${className}`}>
<div className={styles.imgBlock_clipped}>
<img className={styles.imgBlock_img}
src={src}
alt={alt} />
</div>
</div>
)};
export default maskedImgBlock;

Related

Prop value passed in template isn't rendering the actual value, but instead prints out the prop name

I'm in the process of learning React and dabbling with props. I have a component where I've added these props and I'm then trying to give them values in another template.
The issue I'm having is that with my current setup (demoed below), it does't render the value I've passed in the prop, just says the prop code when inspecting.
For example, in Homepage.js, I have padded a value to the id prop (id="screenOne"). However, when inspecting the page, it prints out id="{sectionID}".
I have followed several different guides (example) to see how to pass props, but none of them worked.
Screen.js:
import React from 'react';
import { Container, Row, Col } from 'react-bootstrap';
function Screen(props) {
const sectionID = props.id;
const tag = props.tag;
const header = props.header;
return(
<div className="panel" id="{sectionID}">
<Container>
<Row className="justify-content-center align-items-center">
<Col xs={12} md={6}>
<div className="panel__left">
<h1 className="panel__left-title">
<span className="d-block">{tag}</span>
<span className="d-block">{header}</span>
</h1>
</div>
</Col>
</Row>
</Container>
</div>
)
}
export default Screen;
Homepage.js
import React from "react";
import Screen from "../components/Screen/Screen";
class Homepage extends React.Component {
render(){
return (
<section id="project">
<Screen
id="screenOne"
tag="tag"
header="header"
/>
</section>
);
}
}
export default Homepage;
App.js
import React from "react";
import Homepage from "./pages/Homepage";
function App() {
return (
<Homepage />
);
}
export default App;
You need to pass the prop like this: id={sectionID}.
By including the quotes: id="{sectionID}" you're saying the prop value is the literal string "{sectionID}", which is exactly the problem you've observed.

How to use Header Footer in Reactjs

I am new in Reactjs(Nextjs), I want to create file "header" and "footer" so i can use this file in all pages,So i want to know how can i do this and which is better option
Should i create "Layout.js" and then call Header in this file
Or should i use "Header" and "footer" in _app.js (without create layout file)
Here is my 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>
);
}
}
or there is any other way,How can i do this,Thank you in advance.
Please refer this documentation for basic layout feature in NextJs
First create Layout component
import Header from './header'
import Footer from './footer'
export default function Layout({ children }) {
return (
<>
<Header/>
<main>{children}</main>
<Footer />
</>
)
}
Import and use the <Layout> component in the entry file,
// pages/_app.js
import Layout from '../components/layout'
export default function MyApp({ Component, pageProps }) {
return (
<Layout>
{/* All your page components */}
</Layout>
)
}
This will wrap your page components in the Header and Footer included in the Layout.
First create your Header & Footer then if you are using React.js go to App and put header and footer there like below:
const App = () => {
return (
<>
<Header />
// your routes go here
<Footer />
</>
)
}
For Next.js go to _app.tsx/jsx the entry point of all your pages
export default function MyApp({ Component, pageProps }) {
return (
<>
<Header />
<Component {...pageProps} />
<Footer />
</>
);
}
if you want to make different layouts or isolate that layout in a separate component you can do so.

How to pass mui-icon component as props to another component?

import React from "react";
import "./SideBarPanel.css";
import AddIcon from "#mui/icons-material/Add";
import ExpandMoreIcon from '#mui/icons-material/ExpandMore';
const SideBarIcon = () => {
return (
<div class="sidebar-icon group">
<AddIcon/>
</div>
);
};
const SideBarPanel = () => {
return (
<div className="sidebar-icons">
<SideBarIcon />
<SideBarIcon />
</div>
);
};
export default SideBarPanel;
I want to pass the ExpandMoreIcon into the second SideBarIcon component.
You should be able to pass a component down to a child component via props just like a normal variable. There is one caveat though: when you render it in the child component, you have to ensure it is named with first letter a capital letter.
import React from "react";
import "./SideBarPanel.css";
import AddIcon from "#mui/icons-material/Add";
import ExpandMoreIcon from '#mui/icons-material/ExpandMore';
const SideBarIcon = (props) => {
const {icon: Icon} = props;
return (
<div class="sidebar-icon group">
{Icon && <Icon />}
<AddIcon/>
</div>
);
};
const SideBarPanel = () => {
return (
<div className="sidebar-icons">
<SideBarIcon />
<SideBarIcon icon={ExpandMoreIcon} />
</div>
);
};
export default SideBarPanel;
Here I assign icon to a new variable Icon while destructuring. This is important because <icon /> will not work because the first letter is not capitalized.
Sandbox
Source: https://reactjs.org/docs/jsx-in-depth.html#user-defined-components-must-be-capitalized
I would write <Icon /> as a child of <SideBarIcon />. You can have it as optional child. Also in this case you would approach component composition, which is recommended by React.

How do I disable a button in React for just one option if the button is a reusable React component?

Still learning React to the best of my ability so forgive me if this code isn't the most logical.
I have made a reusable CustomButton component in React to use across my app. I've then created 3 Card components on a Welcome page with each Card nesting this CustomButton inside of it, with each CustomButton using Link from react-router to navigate to different pages of the app. For the 3rd card, I would like to have this button disabled as this particular page will be 'coming soon'. I've been able to add a className to Link within props of the last Card to set the opacity of the text to look inactive, but want to be able to customise the entire button (not just the text within) to make it look greyed out etc.
Is there a simple way to do this?
This is my code so far:
Card:
import React, { useState } from 'react';
import { Card } from 'react-bootstrap';
import CustomButton from './CustomButton';
const WelcomeCard = (props) => {
return (
<>
<Card className='m-3 p-2 welcome-card rounded'>
<Card.Body className='welcome-card__body'>
<Card.Text>{props.text}</Card.Text>
<CustomButton link={props.link} />
</Card.Body>
</Card>
</>
);
};
export default WelcomeCard;
CustomButton:
import React from 'react';
const CustomButton = (props) => {
return <button className='custom-button'>{props.link}</button>;
};
export default CustomButton;
WelcomeScreen - The last component at the bottom is where I want to disable this CustomButton:
import React, { useState } from 'react';
import { Row, Col } from 'react-bootstrap';
import WelcomeCard from '../components/WelcomeCard';
import Loader from '../components/Loader';
import { Link } from 'react-router-dom';
const WelcomeScreen = () => {
const [loading, setLoading] = useState(false);
return (
<>
<div class='welcome-container'>
<div>
<h1 className='p-2 my-3 welcome-header'>Welcome</h1>
</div>
<div className='cards-container'>
<Row>
<Col>
<WelcomeCard
link={
<Link className='cards-link' to='/company'>
Company
</Link>
}
/>
</Col>
<Col>
<WelcomeCard
link={
<Link className='cards-link' to='/landscape'>
Landscape
</Link>
}
/>
</Col>
<Col>
<WelcomeCard
link={
<Link className='cards-link disabled-link' to='/insights'>
AI Insights
</Link>
}
/>
</Col>
</Row>
</div>
</div>
</>
);
};
export default WelcomeScreen;
Thanks in advance for any guidance!
Why you don't use props to solve this problem?
Edit your CustomButton like this:
import React from 'react';
const CustomButton = (props) => {
if(!props.active) return <button className='custom-button-disabled'></button>;
return <button className='custom-button'>{props.link}</button>;
};
export default CustomButton;
You can pass this prop into the CustomButton Component now like you are doing with the link prop.

How to send state from Child component to another component in React

I have a problem with sending state from Child to Parent.
After clicking on Menu in Main component I want to change state active and send this active to Sidebar component, because I want to hide/show Sidebar depends on active class from css. It is easy in Vanilla JS but in React I am little confused.
Main:
import Menu from "./Menu";
import "./Styles.css";
import teamsdb from "./teamsdb";
const Main = ({ name }) => {
return (
<div>
<Menu />
<h1>Main</h1>
<h4>{teamName}</h4>
</div>
);
};
Menu:
const Menu = () => {
const [active, setActive] = useState(false);
const changeMenu = () => {
setActive(!active);
};
return <button onClick={changeMenu}>Menu</button>;
};
export default Menu;
Sidebar:
const Sidebar = () => {
const [searchTerm, setSearchTerm] = React.useState("");
const handleChange = e => {
setSearchTerm(e.target.value);
console.log("Search: ", e.target.value);
};
return (
<div className={active ? 'sidebar active' : 'sidebar'}>
<div className="sidebar__header">
Header
<button>Colors</button>
</div>
<div className="sidebar__search">
<input
type="text"
placeholder="Search"
value={searchTerm}
onChange={handleChange}
/>
</div>
<Teams search={searchTerm} />
</div>
);
};
App.js
import React from "react";
import "./App.css";
import Sidebar from "./components/Sidebar";
import Main from "./components/Main";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
function App() {
return (
<div className="App">
<div className="app__body">
<Router>
<Sidebar />
<Switch>
<Route path="/team/:teamId">
<Main />
</Route>
<Route exact path="/">
<Main />
</Route>
</Switch>
</Router>
</div>
</div>
);
}
You should lift your state up the first common ancestor component, in your case: App. This way you can use it in all its descendants by passing the state and the mutation function (setActive) as prop:
App // Put active state here, pass down active and setActive as prop
^
|
+----+-----+
| |
+ +
Sidebar Main // Pass down active and setActive as prop
^
|
|
Menu // Use setActive to modify state in App
This is explained in React documentation: https://reactjs.org/docs/lifting-state-up.html
With the context api and hooks prop drilling isn’t required anymore. You can simply wrap the parent and child in a context provider and leverage react’s useContext hook for managing state across the 2 components. kent c dodds has a good article with examples here

Categories