Passing Children Styling Down to a Styled Component - javascript

I am making a generalized version of a styled component to replace duplicated code in my codebase. The component looks something like this:
const CustomDiv = styled.div`
display:flex;
b {
padding-right:5px
}
`;
const DivWrapper = ({style, children}) => {
return (
<CustomDiv style={style}>
{children}
</CustomDiv>
);
};
I want to be use this "DivWrapper" in other places of the code base but be able to change the b padding in other places when using it. I tried extending it like this:
const SuperCustomDiv = styled(CustomDiv)`
b {
padding-right:10px
}
`;
I am using the styled components library for React.

You can achieve this by using css props and passing props from the styled-component to the CSS. So, we create styled CSS with some property, then this style place to both styled components. With props, we will pass the value to main style.
import styled, { css } from "styled-components";
const PaddingStyle = (value) => css`
padding-right: ${value}px;
`;
export const SuperCustomDiv = styled.b`
${PaddingStyle((props) => props.padding)}
`;
export const CustomDiv = styled.div`
display: flex;
b {
${PaddingStyle((props) => props.padding)}
}
`;
export const DivWrapper = ({ padding = 50, children }) => {
return (
<CustomDiv as="div" padding={padding}>
<b>Name:</b> {children}
</CustomDiv>
);
};
export default function App() {
return (
<div className="App">
<DivWrapper>John Doe</DivWrapper>
<SuperCustomDiv padding={30}>Super</SuperCustomDiv>
<span>John Smith</span>
</div>
);
}

Related

How to set a value to a specific component in ReactJs

I am trying to make a tic tac toe game using reactjs. I have a function named setSquareValue to set the value of squares to X but when I am clicking on a square all the other squares also filling with X.
The hierarchy is Game.js -> Board.js -> Square.js. I am using useState() which is in Game.js and also the function setSquareValue is in Game.js. I am passign the function to Square.js through Board.js.
Here is my codes:
Game.js
import React, { useState } from 'react';
import './Game.css'
import Board from './Board';
const Game = () => {
const [value, setValue] = useState(null);
const setSquareValue = () => {
setValue("X")
}
return (
<div className='game'>
<h1 style={{color: "#fff"}}>Tic Tac Toe</h1>
<h4 style={{color: "#2D033B"}}>Winner: </h4>
<Board value={value} setSquareValue={setSquareValue} />
</div>
);
};
export default Game;
Board.js
import React from "react";
import Square from "./Square";
import "./Board.css";
const Board = ({ value, setSquareValue }) => {
const squareKeys = [1, 2, 3, 4, 5, 6, 7, 8, 9];
return (
<div className="board">
{squareKeys.map(squareKey => (
<Square
key={squareKey}
value={value}
setSquareValue={setSquareValue} />
))}
</div>
);
};
export default Board;
Square.js
import React from "react";
import "./Square.css";
const Square = ({ value, setSquareValue }) => {
return (
<div className="square" onClick={() => setSquareValue()}>
{value}
</div>
);
};
export default Square;
I want, when I click a certain square, only that square fill with X. But every square is filling with X. How can I solve this?
I tried but X is appearing in every box of the game.
Edited: Added CSS codes
Game.css
.game {
background-color: #AD7BE9;
width: 100%;
height: 100vh;
padding: 2rem;
display: flex;
flex-direction: column;
align-items: center;
}
Board.css
.board {
background-color: #645CBB;
color: #fff;
padding: 1rem;
margin-top: 1rem;
border-radius: 5px;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
Square.css
.square {
border: 1px solid #fff;
height: 5rem;
width: 5rem;
font-size: 20px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
You are passing the value to every single square in your map function, for the solution you can move your value state and setSquareValue function to square component so that every square itself has its own value.
updated Square.js:
import React from "react";
import "./Square.css";
const Square = () => {
const [value, setValue] = useState(null);
const setSquareValue = () => {
setValue("X")
}
return (
<div className="square" onClick={setSquareValue}>
{value}
</div>
);
};
export default Square;
also you don't need to pass value and setSquareValue from Game.js to Board.js and Square.js anymore;
You are creating a unique state for all of the square in Game component. so if you change any time all of the square state will change so this problem occurs. So what you can do is instead of common state, you can create an individual state in Square component as:
CODESANDBOX LINK
const Square = () => {
const [value, setValue] = useState(null);
const setSquareValue = () => {
setValue("X");
};
return (
<div className="square" onClick={setSquareValue}>
{value}
</div>
);
};
The problem is where you are tracking value. Move the logic to store the value state from Game.js to Square.js
When your app begins, there is one instance of Game which has an initial value set to value. Then you pass this single value to Board, where it renders that same value 9 times. Likewise, when you change that single value, it gets changed in Game, then passed to Board, then finally rendered 9 times.
Let Square store its own state.
It is because you are using only one state variable for all square:
const [value, setValue] = useState(null);
You can declare as array variable that length is 9 for 9 square.
Game.js
import React, { useState } from 'react';
import './Game.css'
import Board from './Board';
const Game = () => {
const [value, setValue] = useState(Array(9).fill(''));
const setSquareValue = (index) => {
let values = [...value];
values[index] = 'X';
setValue(values);
}
return (
<div className='game'>
<h1 style={{color: "#fff"}}>Tic Tac Toe</h1>
<h4 style={{color: "#2D033B"}}>Winner: </h4>
<Board value={value} setSquareValue={setSquareValue} />
</div>
);
};
export default Game;
Board.js
import React from "react";
import Square from "./Square";
import "./Board.css";
const Board = ({ value, setSquareValue }) => {
const squareKeys = [1, 2, 3, 4, 5, 6, 7, 8, 9];
return (
<div className="board">
{squareKeys.map((squareKey, index) => (
<Square
key={squareKey}
value={value[index]}
ind = {index}
setSquareValue={setSquareValue} />
))}
</div>
);
};
export default Board;
Square.js
import React from "react";
import "./Square.css";
const Square = ({ value, ind, setSquareValue }) => {
return (
<div className="square" onClick={() => setSquareValue(ind)}>
{value}
</div>
);
};
export default Square;

Is there a way to import a JS function inside a React component?

I've been trying to create a React HOC that will apply the corresponding styles to any component pass to it.
My idea was to do something similar to this
// Button.css.js
const styles = {backgroundColor:"blue"}
// Button.js
const Button = (props) => <button {...props}/>
// applyStyles.js
const applyStyles = (Component) => (props) => {
const styles = import style from `${Component.name}.css`
return <Component {...props} style={styles} />
}
I know applyStyles contains invalid syntax but is just to illustrate what is what I'm trying to do.
If any of you have a way around this I will really appreciate it.
You can try this
import (`/pathTofile/${Component.name}.css`)
.then(data => {
// rest of the code goes her
})
My recommendation is that you use styled-component for this:
const Wrapper = styled.div`
> * {
backgroundColor:"blue"
}`
function AppyStylesHOC(Component) {
return (props) => {
return (<Wrapper>
<Components {...props} />
</Wrapper>
})
}
You can try this:
Button.js
import React from 'react';
import styles from './Button.module.css';
const Button = props => {
return (
<button
className={styles.button}
type={props.type || 'button'}
onClick={props.onClick}
>
{props.children}
</button>
);
};
export default Button;
Button.module.css
.button {
font: inherit;
border: 1px solid #4f005f;
background: #4f005f;
color: white;
padding: 0.25rem 1rem;
cursor: pointer;
}
App.js
import Button from '../Button';
...
<Button type="submit">+ Add</Button>
...

How to render multiple components with different component name?

I have a few components, they have the same parameter with iterative values, like this:
import React from "react";
import Panel from "./Panel";
import Navbar from "./Navbar";
export default function App() {
return (
<div className="App">
<Panel id={1} />
<Navbar id={2} />
</div>
);
}
const Panel = ({ id }) => {
return (
<div>The id is {id}</div>
);
};
const Navbar = ({ id }) => {
return (
<div>The id is {id}</div>
);
};
Working example here: https://codesandbox.io/s/staging-pond-mpnnp
Now I'd like to use map to render those components at once in App.js, something like this:
export default function App() {
const compnentArray = ['Panel', 'Navbar'];
const RenderComponents = () => {
let _o = [];
return (
componentArray.map((item, index) => _o.push(<{item} id={index} />))
)
}
return (
<div className="App">
{RenderComponents()}
</div>
);
}
So that item renders component names. Is this possible?
Sure, you could make use of Array.map()'s second parameter which gives you the index in the array:
import React from "react";
import Panel from "./Panel";
import Navbar from "./Navbar";
const components = [Panel, Navbar];
export default function App() {
return (
<div className="App">
{components.map((Component, i) => (
<Component key={i} id={i + 1} />
))}
</div>
);
}
As mentioned in React's documentation, to render a component dynamically, just make sure you assign it to a variable with a capital first letter and use it like you'd use any other component.
You could swap strings with your actual component references and itererate over them directly in your JSX part, like this :
export default function App() {
const componentsArray = [Panel, Navbar];
return (
<div className="App">
{componentsArray.map((Component, index) => <Component key={index} id={index + 1} />)}
</div>
);
}
Though I would suggest to memoize them to improve performance once you're confortable enough with React to start using memoization.
import React from "react";
import Panel from "./Panel";
import Navbar from "./Navbar";
const components = [Panel, Navbar]; // notice you are using the components as items, not strings;
/*
if the components need props from the parent,
the `renderComponents()` function should be declared
inside the parent component (and possibly with a `useCallback()`
hook, to avoid unnecessary re-declarations on re-renders)
*/
function renderComponents() {
return components.map((comp, index) => <comp key={index} id={index} />) || null;
}
export default function App() {
return (
<div className="App">
{renderComponents()}
</div>
);
}

How to send values React Hook(props)

I made My code.
When Click the button A, appear AAA.
Or Click the button B, appear BBB, Click the button C, appear CCC.
// Main > RightMain.js
import React, { useState } from 'react'
function RightMain() {
const [screen, setScreen] = useState('');
const A = () => {
setScreen('A')
}
const B = () => {
setScreen('B')
}
const C = () => {
setScreen('C')
}
return (
<div>
<button onClick={A}>A</button>
<button onClick={B}>B</button>
<button onClick={C}>C</button>
{screen === 'A' && <div>AAA</div>}
{screen === 'B' && <div>BBB</div>}
{screen === 'C' && <div>CCC</div>}
</div>
)
}
export default RightMain
And I wanna separate My Code(RightMain.js).
When I Click the Button on the RightMain.js.
The Result appear's on the Formations.js like the image below.
But I don kno how to bring value(RightMain.js's screen) to the Formations.js.
// Main > LeftMain.js
import React from 'react'
import RadioBtn from './LeftMain/RadioBtn';
import Formation from './LeftMain/Formation';
function LeftMain() {
return (
<div>
<div>
<RadioBtn />
</div>
<div>
<Formation />
</div>
</div>
)
}
export default LeftMain
//Main > LeftMain > Formation.js
import React, { useState } from 'react'
import RightMain from '../RightMain';
function Formation() {
return (
<div>
</div>
)
}
export default Formation
Thx
If I understand correctly, LeftMain and RightMain are sibilings, and Formation is a child of LeftMain.
One possible approach is to use Context API.
Something like this should work:
// Define the default value
// or return null and take that into consideration when using "useContext"
export const MyCurrentScreenContext = React.createContext({
setScreen: () => void 0,
screen: ''
});
export const MyCurrentScreenProvider = props => {
const [screen, setScreen] = useState('');
const value = useMemo(() => ({ screen, setScreen }), [screen, setScreen]);
return (
<MyCurrentScreenContext.Provider value={value}>
{props.children}
</MyCurrentScreenContext.Provider>
);
}
const Main = () => {
...
return (
<MyCurrentScreenProvider>
<LeftMain />
<RightMain />
...
</MyCurrentScreenProvider>
);
}
const RightMain() {
const { setScreen } = useContext(MyCurrentScreenContext);
....
};
const Formation() {
const { screen } = useContext(MyCurrentScreenContext);
....
};
Read more about context api at the official docs
From what I understand, you want to pass the values down to the child components. If that is correct then you could pass them as parameters when calling it and using props to receive them inside the child component. Something like this.
<div>
<RadioBtn randomVal="value" />
</div>

How to make a react js element by using props?

I have a functional element in react js like this,
function FilterOptions() {
const [isShown, setIsShown] = useState(false);
return (
<div className="filter__options">
{["Category", "Design", "Size", "Style"].map((ourOption) => (
<div
onMouseEnter={() => setIsShown(true)}
onMouseLeave={() => setIsShown(false)}
className="filter__options__container"
>
<div className="filter__options__button">
{ourOption}
</div>
{isShown && <div className="filter__options__content"> Here I want to return the element using props </div>}
</div>
))}
</div>
);
}
I have created a files called, Category.js, Design.js, Size.js, Style.js.
Now I want to use the props so that I can concatenate like this <{ourOption}> <{ourOption}/> so that this will return element.
Any idea how to do this guys?
Choosing the Type at Runtime
First: Import the components used and create a lookup object
import Category from 'Category';
import Design from 'Design';
import Size from 'Size';
import Style from 'Style';
// ... other imports
const components = {
Category,
Design,
Size,
Style,
// ... other mappings
};
Second: Lookup the component to be rendered
function FilterOptions() {
const [isShown, setIsShown] = useState(false);
return (
<div className="filter__options">
{["Category", "Design", "Size", "Style"].map((ourOption) => {
const Component = components[ourOption];
return (
...
<div className="filter__options__button">
<Component />
</div>
...
))}}
</div>
);
}
Alternatively you can just import and specify them directly in the array to be mapped.
function FilterOptions() {
const [isShown, setIsShown] = useState(false);
return (
<div className="filter__options">
{[Category, Design, Size, Style].map((Component) => (
...
<div className="filter__options__button">
<Component />
</div>
...
))}
</div>
);
}
Instead of strings you could iterate over Array of Components
{[Category, Design, Size, Style].map((Component) => (
<Component/>
);
Ill do this as react document
//create components array
const components = {
photo: Category,
video: Design
.....
};
{
Object.keys(components).map((compName) => {
const SpecificSection = components[compName];
return <SpecificSection />;
})
}
Here is a small sample code that you can work with. Use direct component instead of trying to determine by strings.
const Comp1 = () => {
return <p>Comp1 Here</p>
}
const Comp2 = () => {
return <p>Comp 2 Here</p>
}
export default function App() {
return (
<div className="App">
{[Comp1, Comp2].map(Komponent => {
// use Komponent to prevent overriding Component
return <Komponent></Komponent>
})}
</div>
);
}

Categories

Resources