react 组件引用组件_自定位React组件

 2023-09-06 阅读 16 评论 0

摘要: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 proper

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 组件引用组件

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/3/7266.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息