Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React responsive layout without CSS

I'm wondering what is the best approach to implement layouting in React app.

Basics

Let's say we want to have 4 components laid out in simple grid. The most basic way would be something like this.

<svg width={width} height={height} viewBox={`0 0 ${width} ${height}`}>   <A color="red" x={0} y={0} width={width/2} height={height/2} />   <B color="blue" x={width/2} y={0} width={width/2} height={height/2} />   <B color="green" x={0} y={height/2} width={width/2} height={height/2} />   <A color="yellow" x={width/2} y={height/2} width={width/2} height={height/2} /> </svg> 

http://codepen.io/anon/pen/OWOXvV?editors=0010

It will work fine, but typing explicit size values is error-prone and not dev-friendly. What if we could use percentage values (0 - 1) instead?

Simple container

const Container = ({x, y, width, height, children}) => {   return (     <g transform={`translate(${x}, ${y})`}>       {React.Children.map(children, (child) => React.cloneElement(child, { // this creates a copy         x: child.props.x * width,         y: child.props.y * height,         width: child.props.width * width,         height: child.props.height * height       }))}     </g>   ); };   <svg width={width} height={height} viewBox={`0 0 ${width} ${height}`}>   <Container width={width} height={height}>{/* one root container is given real pixel size */}     <Container width={1/2}>{/* it's children recursively use 0-1 coordinates */}       <A color="red" height={1/2} />       <B color="green" y={1/2} height={1/2} />     </Container>     <Container x={1/2} width={1/2}>       <B color="blue" height={1/2} />       <A color="yellow" y={1/2} height={1/2} />     </Container>   </Container> </svg> 

http://codepen.io/anon/pen/PWEmVd?editors=0010

In this case we'll allow Container component to map it's children relative values to real pixel values. It's much easier to use.

Layout container

Another step would be to create layout container, f.e. HContainer that simply lays its children horizontally.

const HContainer = ({ x, y, width, height, children }) => {   const c = React.Children.toArray(children);   const ratio = width / c.reduce((sum, child) => (sum + child.props.width), 0);   return (     <g transform={`translate(${x}, ${y})`}>       {c.reduce((result, child) => {         const width = child.props.width * ratio;         result.children.push(React.cloneElement(child, { // this creates a copy           x: result.x,           y: 0,           width,           height         }));         result.x += width;         return result;       }, { x: 0, children: [] }).children}     </g>   ); };  <svg width={width} height={height} viewBox={`0 0 ${width} ${height}`}>   <HContainer width={width} height={height}>{/* one root container is given real pixel size */}     <Container width={1/4}>{/* it's children recursively use 0-1 coordinates */}       <A color="red" height={1/2} />       <B color="green" y={1/2} height={1/2} />     </Container>     <VContainer width={3/4}>       <B color="blue" />       <A color="yellow" />       <HContainer height={1/2}>         <B color="pink" />         <A color="violet" width={3} />         <B color="#333" />       </HContainer>     </VContainer>   </HContainer> </svg> 

http://codepen.io/anon/pen/pRpwBe?editors=0010

Responsive components

Let's say we'd like some components be removed when width or height is below some value. You'd probably use conditional rendering like this.

const MinWidth = ({ children, width, minWidth, ... others }) => {   return minWidth > width ? null : <Container width={width} {... others }>{ children }</Container>; };  <svg width={width} height={height} viewBox={`0 0 ${width} ${height}`}>   <HContainer width={width} height={height}>{/* one root container is given real pixel size */}     <Container width={1/4}>{/* it's children recursively use 0-1 coordinates */}       <A color="red" height={1/2} />       <B color="green" y={1/2} height={1/2} />     </Container>     <VContainer width={3/4}>       <B color="blue" />       <MinHeight height={1} minHeight={80}>         <A color="yellow" />       </MinHeight>       <HContainer height={1/2}>         <B color="pink" />         <A color="violet" width={3} />         <MinWidth width={1} minWidth={60}>           <B color="#333" />         </MinWidth>       </HContainer>     </VContainer>   </HContainer> </svg> 

http://codepen.io/anon/pen/dNJZGd?editors=0010

But this leaves empty spaces where skipped components used to be. Layout containers should be able to expand rendered components to fill available space.

Responsive layout

And here's the tricky part. I can see no other way to see if component will render, but to instantiate and render it (and it's children). Then, if I lay 3 child components within avaialable space and discover that 4th should not be rendered I'll have to re-render previous 3. It feels like breaking React flow.

Does anyone have any ideas?

like image 461
m1gu3l Avatar asked Jan 31 '17 15:01

m1gu3l


People also ask

Can I use React without CSS?

The entire goal of this article was to make it clear that you are not required to use CSS-in-JS with React, that React works with plain CSS just fine.

How do I make my React page responsive?

Getting started with react-responsive First, begin by creating a new React project with no dependencies. We'll perform an npm install of the react-responsive package with npm i -S react-responsive . Just so you know, react-responsive anticipates different use cases, so we can use it with Hooks or with components.

Is React website responsive?

React-responsive is a media query module that provides CSS media queries in react as a component or hook for responsive web design. This is super useful for rendering or removing specific styled elements in the DOM — restructure your DOM in terms of CSS/Sass Styling, depending on the screen resolution/size.


1 Answers

Use flexbox in inline styles. You are already using inline style from the look of your code. Here is some help

like image 53
J. Mark Stevens Avatar answered Sep 29 '22 18:09

J. Mark Stevens