Here is an online sample https://codesandbox.io/s/jovial-resonance-5mjq0
const Parent = styled.div`
display: flex;
width: 100%;
background: yellow;
justify-content: space-between;
`;
const Children = styled.div`
display: flex;
background: tomato;
height: 50px;
flex: 0 0 10%;
`;
function App() {
return (
<Parent>
<Children />
<Children />
<Children />
<Children />
<Children />
</Parent>
);
}
So regardless the window resizing.
so pure javascript can be done like
document.querySelectorAll(CHILDREN).forEach(e=> {
e.clientWidth
})
but how to get <Children />'s width? I understand it may not common to play DOM in reactjs. But is there a react way to get width? Thanks
Use refs to get DOM elements (after rendering). If you need the width of multiple elements either use several refs and some arithmetic, or use an enveloping parent.
class MyComponent extends React.Component {
constructor() {
super();
this.elementRef = React.createRef();
}
componentDidMount() {
// get the BCR of your ref
const bcr = this.elementRef.current.getBoundingClientRect()
console.log({ width: bcr.width });
}
render() {
return (
<div style={{ width: '234px' }}>
<div ref={this.elementRef} style={{ width: '50%' }}>
Hello, World!
</div>
</div>
);
}
}
ReactDOM.render(<MyComponent />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Related
I have a sample code with one button and 2 images that are shown conditionally. I need to scroll to the top of the content div, where images are shown but it stays in the same position as the previous content.
I tried to separate buttons and content between separate divs but it didn't help. Is there a way to achieve this with only CSS.
Here is the code and working example in codesandbox:
https://codesandbox.io/s/upbeat-sutherland-9r0kh?file=/src/styles.css:0-206
The page:
import { useState } from "react";
import "./styles.css";
export default function App() {
const [isOpen, setIsOpen] = useState(true);
return (
<div className="App">
<div className="buttonContainer">
<button onClick={() => setIsOpen(!isOpen)}>switch</button>
</div>
<div>
{isOpen ? (
<img src="https://images.unsplash.com/photo-1574285013029-29296a71930e?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=927&q=80" />
) : (
<img src="https://images.unsplash.com/photo-1558064340-601a5c6ac442?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=987&q=80" />
)}
</div>
</div>
);
}
CSS:
.App {
font-family: sans-serif;
text-align: center;
display: flex;
align-items: center;
}
.buttonContainer {
position: fixed;
height: 100vh;
}
button {
position: absolute;
height: 100px;
}
I'm actually having trouble doing CSS with the styled component in React. Given below is the code snippet
import React from 'react';
import { Navbar, Container, Row, Col } from 'reactstrap';
import styled from 'styled-components';
const Styles = styled.div`
.navbar {
background-color: black;
position: absolute;
bottom: 0;
width: 100%;
}
.h1 {
color: white;
}
`;
const Footer = () => {
return (
<Styles>
<Navbar>
<Container>
<Row>
<Col sm={{ size: 4 }}>
<h1>Hi</h1>
</Col>
</Row>
</Container>
</Navbar>
</Styles>
);
};
export default Footer;
What I want to do is to change the color of the h1 tag to white but the above custom CSS is not working. I've tried background-color too, but still the issue persists.
With styled-components, you shouldn't use classes for styling elements. You should use separated wrappers for components, it's the main point. I think you wanted to do something like this:
import React from 'react';
import { Navbar, Container, Row, Col } from 'reactstrap';
import styled from 'styled-components';
const StyledNavbar = styled(Navbar)`
background-color: black;
position: absolute;
bottom: 0;
width: 100%;
`;
const Header = styled.h1`
color: white;
`;
const Footer = () => {
return (
<StyledNavbar>
<Container>
<Row>
<Col sm={{ size: 4 }}>
<Header>Hi</Header>
</Col>
</Row>
</Container>
</StyledNavbar>
);
};
export default Footer;
you used .h1 the class, not h1 the tag, in your css.
https://styled-components.com/docs/basics#pseudoelements-pseudoselectors-and-nesting
I have a view and inside it a map function:
<View style={{width: 500, height: 500}}>
{[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15].map((x,i) => {
return(
<View style={{width: 50, height: 50, backgroundColor: 'red'}}></View>
)
})}
</View>
How can I map this into a pattern? Like 5 on a row?
You can achieve this using flexbox and flexWrap, see the below given example
<View style={{display: "flex"; flexDirection: "row", flexWrap: "wrap", flex: 1;}}>
</View>
You can read this documentation to achieve this
https://reactnative.dev/docs/flexbox
You may split your source array into chunks (rows) of desired length and use nested .map() loops to render rows and cells within them:
const chunkArr = (arr, size) =>
arr.reduceRight((r,i,_,s) => (r.push(s.splice(0,size)),r),[])
Following is a complete live-demo of that concept:
const { useState } = React,
{ render } = ReactDOM,
rootNode = document.getElementById('root')
const cellData = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
const Matrix = ({cells}) => {
const rows = cells.reduceRight((r,i,_,s) => (r.push(s.splice(0,5)),r),[])
return (
<div className="wrapper">
{
rows.map((row,i) => (
<div key={i} className="row">
{
row.map((cell,j) => (
<div key={j} className="cell">
{cell}
</div>
))
}
</div>
))
}
</div>
)
}
render (
<Matrix cells={cellData} />,
rootNode
)
.wrapper {
display: flex;
flex-direction: column;
}
.row {
display: flex;
flex-direction: row;
}
.cell {
width: 20px;
height: 20px;
margin: 5px;
background-color: red;
color: #fff;
text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>
I have kind of this construction:
<Wrapper activeTextColor="red">
<Text active={true}>Text 1</Text>
<Text active={false}>Text 2</Text>
</Wrapper>
Styled-components should look like this:
const Text = styled.p``;
const Wrapper = styled.div`
${Text} {
${props =>
props.activeTextColor &&
css`
/* How to make color red based on "active" attribute of Text element? */
`}
}
`;
How to access child's props from parent's styled-component here?
Here is a live example
You can't (as far as I can tell). But you can access the parent's props from the child component (the other way around). That seems to accomplish what you are trying to do.
Short answer:
You have to pass the parent prop to the child component.
In a parent component <Wrapper /> you would have to clone your children and pass the activeTextColor to the children:
const StyledWrapper = styled.div``;
class Wrapper extends React.Component {
render() {
const { children, activeTextColor } = this.props;
return (
<StyledWrapper activeTextColor={activeTextColor}>
{React.Children.map(children, child =>
React.cloneElement(child, {
activeTextColor: activeTextColor
})
)}
</StyledWrapper>
);
}
}
Both activeTextColor and active are now accessible from the Text component.
const Text = styled.p`
${props => css`
color: ${props.active ? activeTextColor : "#000"};
`}
`;
Another Option:
In the case above, it might make more sense to go with a ThemeProvider/ThemeConsumer. If you know an activeTextColor is going to be red (maybe you are dealing with design tokens), then access the active colors with:
${props => css`
background: ${props.active ? props.theme.activeTextColor : '#000'};
`}
Detailed Answer (and why someone would want to do this):
This extends the Short Answer above. At some point you might need to access the parent props in the parent component, and both the child and parent props in the child component.
A real world example would be something like Tabs. I have two different styles / variants of Tabs, with both the Tabs container component and Tab needing its own styles depending its own props. It is one component styled two different ways.
Nesting the styled-components won't work. So you end up with something like this.
const StyledTabs = styled.div`
display: flex;
justify-content: flex-start;
${props =>
props.variant === "wizard" &&
css`
justify-content: space-between;
`}
`;
const StyledTab = styled.p`
font-size: 14px;
white-space: nowrap;
font-family: sans-serif;
border: 1px solid #ddd;
padding: 15px;
${props => css`
background: ${props.active ? "#fff" : "#f6f6f6"};
`}
${props =>
props.variant === "box" &&
css`
& {
border-right: 0 none;
&:last-child {
border-right: 1px solid #ddd;
}
border-top: ${props.active
? "2px solid lightseagreen"
: "1px solid #ddd"};
border-bottom: ${props.active ? "none" : "1px solid #ddd"};
}
`}
${props =>
props.variant === "wizard" &&
css`
& {
margin-right: 20px;
text-align: center;
line-height: 40px;
height: 40px;
width: 40px;
border-radius: 50%;
color: ${props.active ? "#fff" : "#000"};
${props.active && "background: lightseagreen"};
}
`}
`;
class Tabs extends React.Component {
render() {
const { children, variant } = this.props;
return (
<StyledTabs variant={variant}>
{React.Children.map(children, child =>
React.cloneElement(child, {
variant: variant
})
)}
</StyledTabs>
);
}
}
class Tab extends React.Component {
render() {
const { children, variant, active } = this.props;
return (
<StyledTab variant={variant} active={active}>
{children}
</StyledTab>
);
}
}
const App = () => (
<div>
<Tabs variant="box">
<Tab active={true}>Tab 1</Tab>
<Tab>Tab 2</Tab>
<Tab>Tab 3</Tab>
</Tabs>
<Tabs variant="wizard">
<Tab active={true}>Step 1</Tab>
<Tab>Step 2</Tab>
<Tab>Step 3</Tab>
</Tabs>
</div>
);
render(<App />, document.getElementById("root"));
Full example:
https://codesandbox.io/s/upbeat-thunder-wfo2m
Related issue on styled-component's GitHub:
https://github.com/styled-components/styled-components/issues/1193
There are quite a few related questions on StackOverflow, but I don't think there are many clear answers:
react-native with styled-components parent prop
How to style a child component from a parent styled component, considering passed props
const Text = styled.p`color: ${props => props.active ? "red" : "palevioletred"};`;
I want to show different data or hide component if it doesn't fit on screen.
I made a working example but i don't think it's a right/elegant way to do it.
Maybe someone can show me a better way?
This image of example that i made in codepen.
1. I want to hide first red block if it's doesn't fit in grey.
I don't want to do it on media queries of window-size because my red blocks maybe be different in size from user to user.
Codepen example (resize to hide block): https://codepen.io/bofemptiness/pen/YJRKGj
const styled = styled.default
const Component = React.Component;
const DivHat = styled.div`
position: fixed;
top: 0;
width: 100% ;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
background-color: blue;
`
const DivProfile = styled.div`
display: flex;
height: 100%;
cursor: pointer;
`
const ExpCooDiv = styled.div`
display: flex;
flex-direction: column;
font-size: 1rem;
`
class App extends Component {
constructor(props) {
super(props)
this.state = { showCookies: true, lvlRefLastSize: 0 }
this.statsRef = React.createRef()
this.cocRef = React.createRef()
this.lvlRef = React.createRef()
this.mediaStats = this.mediaStats.bind(this);
}
componentDidMount() {
// Add listner when window resizes
window.addEventListener('resize', this.mediaStats)
// Activate function at least one time on load
this.mediaStats()
}
componentWillUnmount() {
window.removeEventListener('resize', this.mediaStats)
}
// Show/hide first red block if summ of red blocks widths <= width of grey one
mediaStats = () => {
console.log(this.statsRef.current.scrollWidth)
if (this.lvlRef.current.scrollWidth != 0)
this.setState({ lvlRefLastSize: this.lvlRef.current.scrollWidth })
if (this.statsRef.current.scrollWidth <= this.state.lvlRefLastSize + this.cocRef.current.scrollWidth) {
this.setState({ showCookies: false })
} else {
this.setState({ showCookies: true })
}
}
render () {
return(
<DivHat>
<div>Menu</div>
<div id='test' ref={this.statsRef}>
<div ref={this.lvlRef} id='test2'>
{this.state.showCookies &&
<React.Fragment>
<span>DATA that i hide</span>
</React.Fragment>
}
</div>
<div ref={this.cocRef} id='test2'>
ANOTHER DATA
</div>
</div>
<DivProfile >
<div> Profile </div>
</DivProfile>
</DivHat>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))