Layout-Isolated Components

栏目: IT技术 · 发布时间: 4年前

内容简介:The move to component-based development has enabled a large number of really incredible improvements to tools and the front-end ecosystem as a whole. Remember when we were building our apps as a set of screens and pages instead of thinking in components? T

The move to component-based development has enabled a large number of really incredible improvements to tools and the front-end ecosystem as a whole. Remember when we were building our apps as a set of screens and pages instead of thinking in components? The component model has changed this entirely - now, we think of pages as a composition of reusable components. As a result, we've also seen a huge increase in the number of open source UI components being built, published, and reused between applications. This modular approach to UI is vital to us at Visly since it's what enables our product to work with any app right away. In the pre-component era, we would have probably required you to build your whole app inside of Visly from scratch.

However, a lot of CSS layout patterns come from this pre-component era, and I want to discuss with you here how some of those patterns should be deprecated, as they break the modularity and composition assumptions of components.

Layout isolation

Layout-Isolated Components

Before we jump into the properties and patterns we should be avoiding, I think it's worth exploring conceptually what we're trying to avoid. Essentially, we want to avoid any properties on the root element of a component that affect, or are affected by, elements outside of the bounds of that component.

Layout-Isolated Components

I would discourage properties like margin , because they act on elements outside of the component's scope; the same goes for properties like align-self , as it will stretch the width or height of the component depending on the flex-direction of its parent. In contrast, properties like padding are fine, as they are confined to the scope of the component. Basically, if a property depends on, or impacts, other components outside of its scope, I would discourage using it.

Layout-isolated component- A component that is unaffected by the parent it is placed within, and does not itself affect the size and position of its siblings.

Something I think worth reiterating is that this only applies to the root element of a reusable component. So for example, as you can see in the code below, using align-self does not make a component break layout isolation automatically, just if it is placed on the root element.

// Does NOT comform to layout isolation principals
function MyComponent() {
  return (
    <div style={{alignSelf: 'center'}}>
      <div />
    </div>
  )
}

// This component is layout isolated
function MyComponent() {
  return (
    <div>
      <div style={{alignSelf: 'center'}}/>
    </div>
  )
}

What properties make a component break layout isolation?

Any property which affects, or is affected by, elements outside a component scope are not layout isolated properties and should be avoided on the root layer of your component. I'll cover a couple of the most common properties (and why they pose a problem) below.

Align self

The reason align-self is not layout-isolated is that it will apply vertical or horizontal alignment to the component depending on the direction of the container in which it's placed. It's especially bad when using stretch , as setting that as the value means your component will end up stretching either vertically or horizontally depending on its container. Some components are built to be stretched in either direction, but this is not the case for the vast majority of components.

So how do you solve this? Should you stop using align-self ? Not at all! All you need to do to make your component reusable while still using align-self is to allow the parent to pass in the alignment value.

// Don't do this. This component isn't very re-usable
function MyComponent() {
  return <div style={{alignSelf: 'stretch'}}/>
}

// Do this. This component can be used from anywhere
function MyComponent(props) {
  return <div style={{alignSelf: props.alignSelf}}/>
}

Flex properties

Flex (flex-grow & flex-shrink) is a fun - though that may be my Stockholm syndrome speaking - and incredibly powerful property. However, as they say, with great power comes a great amount of bugs. Flex actually breaks layout isolation principles in two ways, which leads to countless bugs. Trust me - I was the go-to "flexbox guy" at Facebook for close to three years, and at least 25% of all problems people had were because they were re-using components with flex properties in contexts they weren't initially designed for.

So what does flex do? Flex describes how a component flexes in the main axis. What defines the main axis? The container. Which by definition makes flex break layout isolation. But even if that wasn't the case, there's a second aspect of flex properties that also make them affect components outside their scope. The amount a component flexes depends on its flex value in relation to the sum of all its siblings' flex values. So if you are expecting a component to flex into all the remaining space, that won't always be true, depending on the other siblings. Because of this, flex properties are doubly bad when it comes to making components reusable.

Layout-Isolated Components

There is one important thing to note here. Flex shrink defaults to 1, which by this definition makes every single component with display: flex break layout isolation. In my opinion, you should change this default because it creates a whole lot more problems than it solves. For this reason, components built in Visly all default to having flex-shrink and flex-grow set to 0.

Just like with align-self , the solution is to move flex out of the component and into a prop that can be controlled by the wrapping component.

// Don't do this. This component isn't very re-usable
function MyComponent() {
  return <div style={{flex: 1}}/>
}

// Do this. This component can be used from anywhere
function MyComponent(props) {
  return <div style={{flex: props.flex}}/>
}

Percentages

Percentage dimensions are like flex, but not quite as dangerous because the dimension they apply to does not depend on the parent component. However the size is by definition based on the percentage of the parent size. This means it can be quite easy to get into situations like the one below, where a button looks great in one context but terrible in another.

Layout-Isolated Components

Percentage sizes being set on the button, by the button, are the problem here. And like all the other examples, it can be fixed by allowing the parent to set a percentage size on the button rather than having the button component itself control that.

// Don't do this. This component isn't very re-usable
function MyComponent() {
  return <div style={{height: '100%'}}/>
}

// Do this. This component can be used from anywhere
function MyComponent(props) {
  return <div style={{flex: props.height}}/>
}

If you're building components in Visly, you'll notice that we allow setting the dimensions of a component to 100% . While I don't personally believe that this something we should be using, because it's so common, we chose to support it in Visly. However, all components built in Visly have the ability to have their dimensions overridden by their parent, from either Visly or code.

Margins

Ever fixed a layout using negative margin? Ever wrapped a component in a div to add some extra margin? Both are quite common and serve a good lesson as to why components themselves should never include margin. The reason margin has this issue and padding doesn't is that margin applies to the space outside of the component, while padding applies to the space within. The amount of margin you want on a component depends on which siblings are next to it and the container where your component appears - highly context-specific knowledge that, if built to be reusable, your component should not know about.

Layout-Isolated Components

While this can be solved, just like all the rest of these examples, by passing margin in as a prop from the parent, we have found a <Spacer/> component to be far superior. Aside from the parent-dependency problems of margin, they have a discoverability problem. Is the spacing due to left margin on this component, or right margin on this other component? It's impossible to know without inspecting the DOM! <Spacer/> components solve this and are also a lot easier to manipulate. Want that same spacing between two other elements? Just copy paste the spacer - much easier than fiddling with margins.

While not exactly the same as code, you can see a lot of these benefits in Visly. We don't support margins at all, instead relying on a spacer component. In an update coming soon, we will also be adding support for stack spacing, a concept where a container can configure the spacing between its children.

Tab index

Tab index doesn't change the visual layout of an element but it does impact the layout of an element for screen readers, or people who prefer navigating interfaces using their keyboard . Because it impacts the order of siblings, by definition it breaks layout isolation. Like the previous example, you'll want to move tab index out of the components' control and into its props so that it can be set by the parent.

Absolute positioning

This one is not as bad as the others, as it typically doesn't break anything; it may just make your component unusable in certain situations. For example, if you make a Notification component, you may be inclined to add a position: absolute rule on it, since notifications pop up on top of other content. However, later on, you may want to show the notifications within the context of a sidebar. In this case, you no longer want to position the notification with absolute coordinates.

Like all the previous examples, you can fix this by passing these styles in from above! You never know where your components may be used in the future. In this example we go further by declaring all the styles that can be set on the component as a subset of CSSProperties .

import { CSSProperties } from 'react'

type Style = Pick<
    CSSProperties,
    'position' | 'left' | 'top' | 'right' | 'bottom'
>

interface Props {
	style: Style
}

// This component can be used from anywhere
function MyComponent(props: Props) {
  return <div style={props.style}/>
}

In conclusion

* { flex-shrink: 0; }
<Spacer/>

The idea for this post came out of a twitter discussion with Max Stoiber who shares a lot of my thoughts on this - you should check out his thoughts on margin .

If you have any questions about Visly or comments on the article, please reach outover email or twitter . If you want to give Visly a try, please request access .


以上所述就是小编给大家介绍的《Layout-Isolated Components》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Effective C++

Effective C++

梅耶 (Scott Meyers) / 侯捷 / 电子工业出版社 / 2011-1-1 / 65.00元

《Effective C++:改善程序与设计的55个具体做法(第3版)(中文版)(双色)》内容简介:有人说C++程序员可以分为两类,读过Effective C++的和没读过的。世界项级C++大师scott Meyers成名之作的第三版的确当得起这样的评价。当您读过《Effective C++:改善程序与设计的55个具体做法(第3版)(中文版)(双色)》之后,就获得了迅速提升自己C++功力的一个契机......一起来看看 《Effective C++》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

SHA 加密
SHA 加密

SHA 加密工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具