react 组件引用组件

While React has ways to break the hatch and directly manipulate the DOM there are very few reasons to do this. We shouldn’t directly manipulate the DOM unless we have a really good reason to. When we need to we should use the ref property. Only as a last resort should we manipulate the DOM directly as well as change state during a render.

尽管React有办法打破僵局并直接操纵DOM,但很少有这样做的理由。 除非我们有充分的理由,否则我们不应该直接操作DOM。 当我们需要时,我们应该使用ref属性。 仅在不得已时,我们才应直接操作DOM并在渲染过程中更改状态。

问题 (The Problem)

The grid snaps at 1024px from a fixed to a fluid grid. We wanted our tutorial tips to be 20px away from their parent element and there wasn’t a way to do this with just css. If the tip was positioned correctly in the fixed grid it would be off when the grid snapped to a fluid view.

网格从固定网格捕捉到流体网格时的分辨率为1024px。 我们希望我们的教程技巧距离其父元素20px,并且没有办法仅靠CSS做到这一点。 如果尖端正确地定位在固定网格中,则当网格捕捉到流体视图时,尖端将关闭。

The tutorial metadata is applied directly in inline styles of the component which has the highest css specificity. This meant that media queries couldn’t solve this problem because media queries would be overridden by css with higher specificity.

教程元数据直接以具有最高CSS特异性的组件的内联样式直接应用。 这意味着媒体查询无法解决此问题,因为媒体查询将被具有更高特异性的css覆盖。

解决方案 (The Solution)

The solution needed to be a single set of metadata and a component that knew where it was so it could change its positioning on the fly. Here’s a video of the final component styles changing.

解决方案需要是一组元数据和一个知道其位置的组件,以便可以即时更改其位置。 这是有关最终组件样式更改的视频。

and the component moving with the viewport resize.

组件随视口调整大小而移动。

Element.getClientRects() (Element.getClientRects())

First things first, we need to know where the parent element is on the page before we can do anything with it. The .getClientRects() method does just that. If you query an element on the DOM and call .getClientRects() it’ll return an object of values with the position, height and width of that element in relation to the viewport of the browser. Give it a try on your end.

首先,我们需要知道父元素在页面上的位置,然后才能对其进行任何操作。 .getClientRects() 方法就是这样做的。 如果在DOM上查询一个元素并调用.getClientRects()它将返回一个值对象,该对象的值,高度和宽度与浏览器的视口有关。 尝试一下。

使用状态组件存储位置 (Using a Stateful Component to store positioning)

We need the component to know where it is at all times. Thinking about that requirement we need a class component that can hold its own state, not a stateless functional component. This is because the user can shrink or expand their viewport past or less than the 1024px threshold which changes our grid to fluid or fixed position. The component needs to be aware of viewport size so it can hold on to its dynamically generated positioning any time the screen size changes.

我们需要组件始终知道它在哪里。 考虑到这一需求,我们需要一个可以保持其自身状态的class组件,而不是无状态的功能组件。 这是因为用户可以缩小或扩展其视口,使其超过或小于1024px阈值,从而将我们的网格更改为流体位置或固定位置。 组件需要注意视口的大小,以便在屏幕大小变化时可以保持其动态生成的位置。

吸气剂和二传手 (Getters and Setters)

The component has two core functions around dynamic positioning. Setting styles dynamically in relation to where the parent element is on the screen and getting those set styles to render the position of the tip. We’ve named these function methods getStyles and setStyles.

该组件具有围绕动态定位的两个核心功能。 相对于父元素在屏幕上的位置动态设置样式,并获取这些设置样式以呈现笔尖的位置。 我们将这些函数方法命名为getStylessetStyles

/*** Method for tutorial tip to dynamically set position based on state.** @return {object} with tutorialTip dynamic position style values*/
, getStyles: function () {var self = this, styles = {top      : self.state.top    || 'auto', bottom   : self.state.bottom || 'auto'// (We'll talk about this positioning later)     , left     : self.state.left   || -9999, right    : self.state.right  || 'auto'};// Hide tutorial tip during transitions to prevent flickering. (We'll talk about this later)if (!this.state.display) {styles.display = 'none';}return styles;
}
view raw
/*** Queries the DOM and dynamically generates values to update state. These values are passed to getStyles* to update positioning.** @return {void} function mutates state.*/, setStyles: function () {var {step} = this.props, meta = tutorialMeta[step]// (We'll talk about this later), el = document.querySelector('.step' + step)// Get queried DOM element's values (top, right, left, width, etc.), bounds = el && el.getBoundingClientRect();if (bounds) {switch (meta.position) {case 'right':this.setState({top: Math.floor(bounds.top - meta.offsetTop), left: Math.floor((bounds.right) + meta.offsetLeft), display: true});break;case 'left':this.setState({top: Math.floor(bounds.top - meta.offsetTop), left: Math.floor((bounds.left) + meta.offsetLeft), display: true});break;case 'bottom':this.setState({top: Math.floor(bounds.top - meta.offsetTop), left: Math.floor((bounds.right - bounds.width) + meta.offsetLeft), display: true});break;case 'bottom-left':this.setState({top: Math.floor(bounds.top - meta.offsetTop), left: Math.floor((bounds.right - bounds.width) + meta.offsetLeft), display: true});break;default:break;}}}

In this particular use cases we load in tutorialMeta JSON data for each tutorial tip and setState accordingly for each tip positioning type. Note: This isn’t a requirement for a self positioning component itself. Just information for the tutorial. Examples are instruction text and offset positioning so the tip is 20px away from it’s parent element and centered.

在此特定用例中,我们为每个教程提示加载tutorialMeta JSON数据,并为每种提示定位类型分别加载setState注意:这不是自定位组件本身的要求。 只是本教程的信息。 示例是指令文本和偏移位置,因此笔尖距离其父元素20px并居中。

Now, it’s time to take this functionality and hook it into React’s lifecycle methods so the component know’s when to update its own positioning.

现在,是时候使用此功能并将其挂接到React的生命周期方法中,以便组件知道何时更新其自身的位置。

连接到React的生命周期方法 (Connecting to React’s Life Cycle Methods)

Let’s hook up our getters and setters so our component knows when to fire them and update its props and state.

让我们连接吸气剂和吸气剂,以便我们的组件知道何时触发它们并更新其道具和状态。

Initialization and Destruction:

初始化和销毁​​:

componentDidMount: function () {window.addEventListener('resize', this.setStyles);this.setStyles();
}
, componentWillUnmount: function () {window.removeEventListener('resize', this.setStyles);
}

On component load we need to setStyles since we currently don’t have any styles to get! Remember, the component is going to call .getClientRect()which is going to dynamically set positioning values. Additionally, we don’t want to query the DOM every time we resize the viewport.

在组件加载时,我们需要setStyles因为我们目前没有任何样式! 记住,该组件将调用.getClientRect() ,它将动态设置定位值。 此外,我们不想每次调整视口大小时都查询DOM。

, shouldComponentUpdate: function (nextProps, nextState) {//  We use use lodash at work for convenice methods like isEqualreturn !_.isEqual(nextProps, this.props) || !_.isEqual(nextState, this.state);}, componentWillReceiveProps: function (nextProps) {if (nextProps.step !== this.props.step) {// Step has changed, hide the tutorial boxthis.replaceState({display: false});}}

We check if our props or state has changed. shouldComponentUpdate's default is to return true if any state changed per React’s docs. Since we’re also getting data from our container component as props we also need to check for props updates. Note: There is global dispatch and data for the entire tutorial like nextStep and currentStep this isn’t a requirement for every use case, just the one that we we’re solving for.

我们检查道具或状态是否已更改。 shouldComponentUpdate的默认值是如果每个React的文档中的任何状态都改变了,则返回true。 由于我们还从容器组件获取数据作为道具,因此我们还需要检查道具更新。 注意:整个教程都具有全局调度和数据,例如nextStepcurrentStep这并不是每个用例的要求,而只是我们要解决的用例。

Next up componentWillRecieveProps is fired before a mounted component receives new props (docs). Using replaceState rather than setState blows away state and sets the component to not show. This is also for a very specific and deliberate for our use case, the flickering problem.

接下来componentWillRecieveProps之前安装的组件接收新道具(被激发的文档 )。 使用replaceState而不是setState吹散状态并将组件设置为不显示。 这也是针对我们的用例(闪烁问题)的一个非常具体和故意的问题。

有一个闪烁的问题 (There was a flickering problem)

The dreaded flicker! It was ever so subtle, but it made our team twitch. There was a flash on initial load and when transitioning the tutorial tip it would hangout just one render step before where it was supposed to be.

可怕的闪烁! 它曾经如此微妙,但却使我们的团队抽搐。 初始加载时出现了闪烁,过渡教程提示时,它会在预期的位置之前仅进行一个渲染步骤。

The Flash Flicker: That’s where the -9999 position came in. If there’s no positioning to give our component just make sure it’s off the page entirely.

Flash Flicker:这是-9999位置。如果没有位置可提供给我们的组件,只需确保它完全不在页面上。

The Hanging Flicker: Every time we get new props the component sets our display to false removing the component from the DOM entirely during transitions. If you look in componentWillRecieveProps , setStyles and getStyles you’ll see reference to how the component is removed and added with display set to false or true.

悬挂的闪烁:每当我们获得新的道具时,组件会将显示设置为错误,从而在过渡期间将组件完全从DOM中移除。 如果查看componentWillRecievePropssetStylesgetStyles ,则会看到有关如何删除和添加组件并将display设置为false或true的参考。

渲染器 (The Render)

This is the function that’s going to get our dynamically generated styles which is called in the styles props. Note: _.getClassNameFromObject is our own custom function that’ll create a string which we can add to a component class styles. We’re not going to dig into this function because it’s out of scope of the post. But, if you’re interested please leave a comment on the bottom of the post and I’ll try and answer your questions.

该函数将获取我们在样式props调用的动态生成的样式。 注意: _.getClassNameFromObject是我们自己的自定义函数,它将创建一个字符串,该字符串可以添加到组件类样式中。 我们不会深入研究此功能,因为它超出了发布的范围。 但是,如果您有兴趣,请在帖子底部留下评论,我将尽力回答您的问题。

, render: function () {let {step} = this.props;var props = this.props, meta = tutorialMeta[step], parentClass = _.getClassNameFromObject({'tutorial-wrap': true}), childClass = _.getClassNameFromObject({'tutorial-tip': true, 'top'     : _.isEqual(_.get(meta, 'position'), 'top'), 'right'   : _.isEqual(_.get(meta, 'position'), 'right'), 'bottom'  : _.isEqual(_.get(meta, 'position'), 'bottom'), 'left'    : _.isEqual(_.get(meta, 'position'), 'left'), 'bottom-left':  _.isEqual(_.get(meta, 'position'), 'bottom-left')}), text = props.error ? meta.error : meta.text, btnCls = _.getClassNameFromObject({'btn btn-special btn--short next': meta.nextButton, 'hidden': !meta.nextButton});if (!props.visible) return null;return (<div className={parentClass}><div className={childClass} style={this.getStyles()}><div><div className="step-info"><span><span className="step"><i className="fa fa-question-circle" aria-hidden="true"></i>&nbsp; Step {props.step + 1}</span> of {tutorialMeta.length}</span></div><div className="step-text"><span dangerouslySetInnerHTML={{__html: text}}></span></div><div className="end-tutorial"><a className="clickable" onClick={props.onTutorialFinish}>End tutorial</a><button className={btnCls} onClick={props.onTutorialNext}>Next</button></div></div></div></div>);}

Here’s a diagram of our component lifecycle, getters, setters and render methods.

这是我们的组件生命周期,getter,setter和render方法的图表。

整个组成部分 (The Entire Component)

var React  = require('react'), _      = require('lodash'), tutorialMeta = require('./tutorialMeta.json').tutorial;/*** Tutorial Component* @class TutorialTip* @param {props} object that holds global data to render component.* @param {element} element to put tutorial tip around.** @return {element} with tutorialTip*/module.exports = React.createClass({getInitialState: function () {return {display: true};}, componentDidMount: function () {window.addEventListener('resize', this.setStyles);this.setStyles();}, componentWillUnmount: function () {window.removeEventListener('resize', this.setStyles);}, shouldComponentUpdate: function (nextProps, nextState) {return !_.isEqual(nextProps, this.props) || !_.isEqual(nextState, this.state);}, componentWillReceiveProps: function (nextProps) {if (nextProps.step !== this.props.step) {// Step has changed, hide the tutorial boxthis.replaceState({display: false});}}
/*** Method for tutorial tip to dynamically set position based on state.** @return {object} with tutorialTip dynamic position style values*/, getStyles: function () {var self = this, styles = {top      : self.state.top    || 'auto', bottom   : self.state.bottom || 'auto', left     : self.state.left   || -9999, right    : self.state.right  || 'auto'};// Hide tutorial tip during transitions to prevent flickering.if (!this.state.display) {styles.display = 'none';}return styles;}, componentDidUpdate: function () {this.setStyles();}
/*** Queries the DOM and dynamically generates values to update state. These values are passed to getStyles* to update positioning.** @return {void} function mutates state.*/, setStyles: function () {var {step} = this.props, meta = tutorialMeta[step], el = document.querySelector('.step' + step)// Get queried DOM element's values (top, right, left, width, etc.), bounds = el && el.getBoundingClientRect();if (bounds) {switch (meta.position) {case 'right':this.setState({top: Math.floor(bounds.top - meta.offsetTop), left: Math.floor((bounds.right) + meta.offsetLeft), display: true});break;case 'left':this.setState({top: Math.floor(bounds.top - meta.offsetTop), left: Math.floor((bounds.left) + meta.offsetLeft), display: true});break;case 'bottom':this.setState({top: Math.floor(bounds.top - meta.offsetTop), left: Math.floor((bounds.right - bounds.width) + meta.offsetLeft), display: true});break;case 'bottom-left':this.setState({top: Math.floor(bounds.top - meta.offsetTop), left: Math.floor((bounds.right - bounds.width) + meta.offsetLeft), display: true});break;default:break;}}}, render: function () {let {step} = this.props;var props = this.props, meta = tutorialMeta[step], parentClass = _.getClassNameFromObject({'tutorial-wrap': true}), childClass = _.getClassNameFromObject({'tutorial-tip': true, 'top'     : _.isEqual(_.get(meta, 'position'), 'top'), 'right'   : _.isEqual(_.get(meta, 'position'), 'right'), 'bottom'  : _.isEqual(_.get(meta, 'position'), 'bottom'), 'left'    : _.isEqual(_.get(meta, 'position'), 'left'), 'bottom-left':  _.isEqual(_.get(meta, 'position'), 'bottom-left')}), text = props.error ? meta.error : meta.text, btnCls = _.getClassNameFromObject({'btn btn-special btn--short next': meta.nextButton, 'hidden': !meta.nextButton});if (!props.visible) return null;return (<div className={parentClass}><div className={childClass} style={this.getStyles()}><div><div className="step-info"><span><span className="step"><i className="fa fa-question-circle" aria-hidden="true"></i>&nbsp; Step {props.step + 1}</span> of {tutorialMeta.length}</span></div><div className="step-text"><span dangerouslySetInnerHTML={{__html: text}}></span></div><div className="end-tutorial"><a className="clickable" onClick={props.onTutorialFinish}>End tutorial</a><button className={btnCls} onClick={props.onTutorialNext}>Next</button></div></div></div></div>);}
});

但是,还有更多! (But wait there’s more!)

We also came up with an interesting solution to avoid adding components all over our application. This is useful if you need to add a series of components to your application like a tutorial.

我们还提出了一个有趣的解决方案,以避免在整个应用程序中添加组件。 如果您需要向应用程序中添加一系列组件(例如教程),这将很有用。

In setStyles we query the DOM for a specific step rather than include the component multiple times. The container component renders the component once, then on each step change we look for a different step class to render the tutorial component.

setStyles我们向DOM查询特定步骤,而不是多次包含组件。 容器组件只渲染一次组件,然后在每次更改步骤时,我们都寻找一个不同的步骤类来渲染教程组件。

那是所有人 (That’s all folks)

Hopefully this helps anyone how might need this type of dynamic positioning functionality in their React application.

希望这可以帮助任何人在他们的React应用程序中可能需要这种类型的动态定位功能。

A big thanks to Dexter engineering specifically Daniel Ilkovich and David Hufor letting me share this code and all of their help and support while building the user tutorial feature on our site.

非常感谢Dexter工程人员,特别是Daniel Ilkovich和David Hu ,让我在构建我们网站上的用户教程功能时,可以共享此代码以及他们的所有帮助和支持。

翻译自: https://www.freecodecamp.org/news/self-positioning-react-components-2/

react 组件引用组件

react 组件引用组件_自定位React组件相关推荐

  1. [react] 同时引用这三个库react.js、react-dom.js和babel.js它们都有什么作用?

    [react] 同时引用这三个库react.js.react-dom.js和babel.js它们都有什么作用? React.js: React中的组件(Component).Context.hooks ...

  2. jsonp react 获取返回值_谈谈对 React 新旧生命周期的理解

    前言 在写这篇文章的时候,React 已经出了 17.0.1 版本了,虽说还来讨论目前 React 新旧生命周期有点晚了,React 两个新生命周期虽然出了很久,但实际开发我却没有用过,因为 Reac ...

  3. react 日期怎么格式化_手写React的Fiber架构,深入理解其原理

    熟悉React的朋友都知道,React支持jsx语法,我们可以直接将HTML代码写到JS中间,然后渲染到页面上,我们写的HTML如果有更新的话,React还有虚拟DOM的对比,只更新变化的部分,而不重 ...

  4. react中样式冲突_如何通过React中的样式使您的应用漂亮

    react中样式冲突 by Vinh Le 由Vinh Le 如何通过React中的样式使您的应用漂亮 (How to make your apps pretty with styling in Re ...

  5. ionic 修改组件默认样式_开源Magpie:组件库详解

    开源项目专题系列(八)1.开源项目名称:magpie_fly2.github地址: https://github.com/wuba/magpie_fly 3.简介:magpie_fly 是58集体出品 ...

  6. ssis 角本组件更新数据_使用SSIS脚本组件作为数据源

    ssis 角本组件更新数据 介绍 (Introduction) SSIS Script component is one data transformation tasks in SQL Server ...

  7. native react 图片多选_开源一个图片组件 react-native-border-radius-image

    大家好,十一假期biu的一下就过去了,相信大家都没有玩的尽兴,没关系,我们还有周六周日双休日(不加班),或者再坚持三个月就到了2020年的元旦了, 是不是发现时间过得太快了. 好了,开工了, . 笔者 ...

  8. react前端显示图片_如何在react项目中引用图片?

    如何在react项目中引用图片?本文码云笔记将为大家整理在react项目中插入图片以及背景图片的方法,希望对需要的小伙伴提供一些参考. 在react项目中插入图片以及背景图片的方法共有2种: 1.im ...

  9. react 替换标签内容_如何在 React 里自定义标签和属性?

    日常开发中通常会有一些需求,需要定义一些全局通用的组件,在 Vue 里是有这样的功能,但是在 React 里,没有见过有类似的做法,通常都是在需要的时候引入组件,原则上在 Jsx 里只允许 html ...

最新文章

  1. DeepChem | Windows 10下anaconda3环境从源码构建并安装deepchem
  2. eBay | 实践Hadoop任务的性能翻倍之路
  3. Duilib嵌入CEF以及JavaScript与C++交互
  4. java 栈 泛型_java 泛型栈(数组实现) | 学步园
  5. Java并发编程以及并发须知的几个概念:什么是线程安全?
  6. 使用WORD封面自带模板?
  7. oracle帮助文档_Spring Boot Config文档,使用IntelliJ IDEA的两种方法
  8. C++ 引用 支持多级嵌套吗
  9. HTML+CSS零基础学习笔记(二)
  10. win2008服务器系统玩红警,如何让Win8顺利兼容红警2
  11. 服务器怎么开账号,大芒果服务器怎么创建GM账号?
  12. 12306抢票工具的使用
  13. flex布局 flex_时髦的Flickr Flex小部件
  14. 保姆级教程:NobePay从注册到充值开卡全过程
  15. windows 通过cmd命令连接wifi
  16. 语音信号短时域分析之短时平均能量(四)
  17. 指纹识别综述(2): 指纹传感器
  18. Z-Wave Mesh 优先路由机制说明
  19. 文件上载限制4gb_新get!百度网盘破除上传单个文件超4GB限制
  20. 清华北大2021毕业生就业报告出炉!清华博士0人出国,70%进体制

热门文章

  1. 2020.10.s1 冯上
  2. 如何添加媒体控件Windows Media Player到工具箱中
  3. selenium-隐式等待和显式等待-0223
  4. 数据结构-二叉树的遍历-前序遍历 中序遍历 后序遍历
  5. openssh升级之后git账户免密登陆失效
  6. maatkit使用总结
  7. 用户控件(UserControl)
  8. 绑定事件的几个方法总结
  9. MySQL数据库优化技术概述
  10. 高速建成Android开发环境ADT-Bundle和Hello World