Рубрики
Без рубрики

Самостоятельное позиционирование

В то время как реагирование имеет способы разрушать люк и напрямую манипулировать домом, есть очень мало причин для этого. Мы не должны напрямую манипулировать домом, если у нас не очень хорошая причина. Когда нам нужно, чтобы мы должны использовать свойство REF. Только как последний

Автор оригинала: Benjamin Schachter.

В то время как реагирование имеет способы разрушать люк и напрямую манипулировать домом, есть очень мало причин для этого. Мы не должны напрямую манипулировать домом, если у нас не очень хорошая причина. Когда нам нужно, чтобы мы должны использовать Ref свойство. Только в качестве последнего курорта мы должны манипулировать домом непосредственно, а также изменять состояние во время рендера.

Эта проблема

Сетка съемки на 1024 пикам из фиксированной к сетке жидкости. Мы хотели, чтобы наши учебные советы были 20 пиками от своего родительского элемента, и не было никакого способа сделать это только с помощью CSS. Если наконечник был правильно расположен в фиксированной сетке, он был бы выключен, когда сетка подхватилась на вид жидкости.

Учебные метаданные наносится непосредственно в встроенных стилях компонента, который имеет самую высокую специфику CSS. Это означало, что средства массовой информации не могут решить эту проблему, потому что средства массовой информации будут переопределены CSS с более высокой специфичностью.

Решение

Решение, необходимое для одного набора метаданных и компонентом, который знал, где это было так, чтобы он мог изменить свое позиционирование на лету. Вот видео окончательного изменения стилей компонентов.

И компонент, движущийся с изменением вида в виду.

Element.getclientRects ()

Первые вещи сначала нам нужно знать, где родительский элемент на странице, прежде чем мы сможем что-нибудь сделать с ним. .getClientRects () Метод только что это. Если вы запрашиваете элемент на DOM и позвоните .getClientRects () Это вернет объект значений с положением, высотой и шириной этого элемента по отношению к ViewPort браузера. Дайте это попробовать на вашем конце.

Использование элементальной компоненты для хранения позиционирования

Нам нужен компонент, чтобы знать, где он всегда. Думая об этом требовании нам нужна Класс Компонент, который может удерживать свое собственное состояние, а не функциональный компонент без гражданства. Это связано с тем, что пользователь может уменьшить или расширить их просмотр в прошлом или меньшего порога 1024PX, который меняет нашу сетку на жидкость или фиксированное положение. Компонент должен быть осведомлен по размеру ViewStort, поэтому он может удерживать его динамически сгенерированное позиционирование в любое время, когда размер экрана изменяется.

Геттерс и поселтели

Компонент имеет два основных функция вокруг динамического позиционирования. Установка стилей динамически по отношению к тому, где родительский элемент находится на экране и получает эти установленные стили для рендеринга позиции наконечника. Мы назвали эти методы функции GetStyles и SetStyles Отказ

/**
 * 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;
      }
    }
  }

В этом конкретном использовании случаи мы загружаем в TutorialMeta JSON DATA для каждого руководства и SetState Соответственно для каждого типа позиционирования наконечника. Примечание: Это не требование для самого позиционирования самого компонента. Просто информация для учебника. Примерами являются инструктивный текст и позиционирование смещения, поэтому наконечник составляет 20 пикселей от его родительского элемента и центрирован.

Теперь пришло время взять эту функцию и подключить его к методам жизненного цикла RACT, поэтому компонент знает, когда обновить свое позиционирование.

Подключение к методам жизненного цикла Rection

Давайте подключим к нашим получателям и сетчатым, поэтому наш компонент знает, когда уволить их и обновлять свои реквизиты и состояние.

Инициализация и разрушение:

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

На компонентную нагрузку нам нужно SetStyles Так как у нас сейчас нет стилей! Помните, компонент собирается позвонить .getClientRect () который собирается динамически устанавливать значения позиционирования. Кроме того, мы не хотим запрашивать DOM каждый раз, когда мы увеличиваем размер просмотра.

  , shouldComponentUpdate: function (nextProps, nextState) {
    //  We use use lodash at work for convenice methods like isEqual
    return !_.isEqual(nextProps, this.props) || !_.isEqual(nextState, this.state);
  }

  , componentWillReceiveProps: function (nextProps) {
    if (nextProps.step !== this.props.step) {
      // Step has changed, hide the tutorial box
      this.replaceState({
        display: false
      });
    }
  }

Мы проверяем, изменились ли наш реквизит или состояние. должен быть необходим По умолчанию для возврата True, если какое-либо состояние изменилось на Regive Документы Отказ Поскольку мы также получаем данные из нашего компонента контейнера в качестве реквизитов, нам также нужно проверить обновления реквизитов. Примечание: Есть глобальная отправка и данные для всего учебника, как NextStep и enterstep Это не требование для каждого случая использования, только тот, который мы будем решать.

Далее ComponentWillReciveProps Уволен перед установленным компонентом получает новые реквизиты ( Документы ). Использование Заменяют а не SetState дует состояние и устанавливает компонент не показывать. Это также для очень специфических и преднамеренных для нашего использования в нашем использовании, проблема мерцающих.

Была мерцающая проблема

Страшный мерцание! Это было так тонко, но это сделало нашу команду дергаться. Там была вспышка на начальной нагрузке и при переходе учешивающего наконечника он будет хотеть только один шаг рендер до того, как он должен был быть.

Flash Flicker: Вот где -9999 Положение пришло. Если нет позиционирования, чтобы дать нашему компоненту, просто убедитесь, что это не полностью.

Повешивающий мерцание: Каждый раз, когда мы получаем новые реквизиты, компонент устанавливает наш дисплей в FALSE, удаляя компонент от дома полностью во время переходов. Если вы посмотрите в ComponentWillReciveProps , SetStyles и GetStyles Вы увидите ссылку на то, как компонент удаляется и добавляется с помощью Дисплей установить значение false или true.

Рендер

Это функция, которая собирается получить наши динамически сгенерированные стили, которые называются в стилях реквизит Отказ Примечание: _.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 (
      
  Step {props.step + 1} of {tutorialMeta.length}
); }

Вот схема нашего компонента жизненного цикла, полученных, устойчивых и представляющих методов.

Весь компонент

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 box
      this.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 (
      
  Step {props.step + 1} of {tutorialMeta.length}
); } });

Но подождите, что есть больше!

Мы также придумали интересное решение, чтобы избежать добавления компонентов по всему нашему приложению. Это полезно, если вам нужно добавить серию компонентов к вашему приложению, как учебник.

В SetStyles Мы запрашиваем DOM для определенного шага, а не включить компонент несколько раз. Компонент контейнеров оказывает компонент один раз, затем на каждом шаге изменяется, мы ищем другой класс пошагового класса, чтобы сделать учебный компонент.

Это все люди

Надеюсь, это поможет кому-нибудь, как может потребоваться этот тип функциональности динамического позиционирования в их приложении React.

Большое спасибо Декстер инженерия специально Даниэль Илькович и Дэвид Ху Чтобы позволить мне поделиться этим кодом и всей их помощью и поддержкой при создании функции учебной пособии пользователей на нашем сайт Отказ