Подтвердить что ты не робот

Отложенный рендеринг компонентов React

У меня есть компонент React с несколькими дочерними компонентами в нем. Я хочу отображать дочерние компоненты не сразу, а после некоторой задержки (одинаковой или различной для каждого из дочерних элементов).

Мне было интересно - есть ли способ сделать это?

4b9b3361

Ответ 1

Я думаю, что самый интуитивный способ сделать это - дать детям "подождать" prop, который скрывает компонент для продолжительности, переданной от родителя. Установив состояние по умолчанию скрытым, React по-прежнему будет отображать компонент немедленно, но он не будет виден до тех пор, пока состояние не изменится. Затем вы можете настроить componentWillMount для вызова функции, чтобы отобразить ее после продолжительности, прошедшей через реквизиты.

var Child = React.createClass({
    getInitialState : function () {
        return({hidden : "hidden"});
    },
    componentWillMount : function () {
        var that = this;
        setTimeout(function() {
            that.show();
        }, that.props.wait);
    },
    show : function () {
        this.setState({hidden : ""});
    },
    render : function () {
        return (
            <div className={this.state.hidden}>
                <p>Child</p>
            </div>
        )
    }
});

Затем в родительском компоненте все, что вам нужно будет сделать, это передать продолжительность, в течение которой вы хотите, чтобы ребенок подождал, прежде чем отображать его.

var Parent = React.createClass({
    render : function () {
        return (
            <div className="parent">
                <p>Parent</p>
                <div className="child-list">
                    <Child wait={1000} />
                    <Child wait={3000} />
                    <Child wait={5000} />
                </div>
            </div>
        )
    }
});

Здесь демо

Ответ 2

В компоненте вашего отца <Father /> вы можете создать начальное состояние, в котором вы отслеживаете каждого дочернего элемента (например, используя и id), присваивая логическое значение, что означает рендер или нет:

getInitialState() {
    let state = {};
    React.Children.forEach(this.props.children, (child, index) => {
        state[index] = false;
    });
    return state;
}

Затем, когда компонент монтируется, вы запускаете свои таймеры для изменения состояния:

componentDidMount() {
    this.timeouts = React.Children.forEach(this.props.children, (child, index) => {
         return setTimeout(() => {
              this.setState({ index: true; }); 
         }, child.props.delay);
    });
}

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

let children = React.Children.map(this.props.children, (child, index) => {
    return React.cloneElement(child, {doRender: this.state[index]});
});

Итак, в вашем <Child /> компоненте

render() {
    if (!this.props.render) return null;
    // Render method here
}

Когда тайм-аут запущен, состояние изменяется, а компонент отца перезагружается. Реквизиты для детей обновляются, и если doRender true, они будут отображаться сами.

Ответ 3

Другой подход для отложенного компонента:

Delayed.jsx:

import React from 'react';
import PropTypes from 'prop-types';

class Delayed extends React.Component {

    constructor(props) {
        super(props);
        this.state = {hidden : true};
    }

    componentDidMount() {
        setTimeout(() => {
            this.setState({hidden: false});
        }, this.props.waitBeforeShow);
    }

    render() {
        return this.state.hidden ? '' : this.props.children;
    }
}

Delayed.propTypes = {
  waitBeforeShow: PropTypes.number.isRequired
};

export default Delayed;

Использование:

 import Delayed from '../Time/Delayed';
 import React from 'react';

 const myComp = props => (
     <Delayed waitBeforeShow={500}>
         <div>Some child</div>
     </Delayed>
 )

Ответ 4

Зависит от вашего варианта использования.

Если вы хотите сделать анимацию для детей, добавьте в нее надстрочную анимацию: https://facebook.github.io/react/docs/animation.html В противном случае сделайте рендеринг детей зависимыми от реквизита и добавьте реквизит после некоторой задержки.

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

Ответ 5

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

По существу, у меня есть сторонний компонент Popover, который использует элемент DOM для привязки в качестве опоры. Проблема в том, что я не могу гарантировать, что якорный элемент будет там сразу, потому что элемент привязки становится видимым одновременно с Popover, к которому я хочу привязать его (во время той же самой отправки).

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

В конечном итоге я решил немного отложить (re) рендер компонента Popover, чтобы убедиться, что элемент DOM привязки найден. Он использует эту функцию в качестве дочернего шаблона, чтобы отображать только дочерние элементы после фиксированной задержки:

import { Component } from 'react'
import PropTypes from 'prop-types'

export default class DelayedRender extends Component {
    componentDidMount() {
        this.t1 = setTimeout(() => this.forceUpdate(), 1500)
    }

    componentWillReceiveProps() {
        this.t2 = setTimeout(() => this.forceUpdate(), 1500)
    }

    shouldComponentUpdate() {
        return false
    }

    componentWillUnmount() {
        clearTimeout(this.t1)
        clearTimeout(this.t2)
    }

    render() {
        return this.props.children()
    }
}

DelayedRender.propTypes = {
    children: PropTypes.func.isRequired
}

Его можно использовать следующим образом:

<DelayedRender>
    {() =>
        <Popover anchorEl={getAnchorElement()}>
            <div>Hello!</div>
        </Popover>
    )}}
</DelayedRender>

Чувствует себя довольно хаки, но работает для моего использования. Тем не менее.

Ответ 6

рендерит дочерние компоненты не сразу, а через некоторое время.

Вопрос говорит о задержке рендеринга, но если все в порядке, но скрыть...

Вы можете сразу визуализировать компоненты с карты, но используйте анимацию css, чтобы задержать их показ.

@keyframes Jumpin {
 0% { opacity: 0; }
 50% { opacity: 0; }
 100% { opacity: 1; }
}
// Sass loop code
@for $i from 2 through 10 {
 .div .div:nth-child(#{$i}) {
  animation: Jumpin #{$i * 0.35}s cubic-bezier(.9,.03,.69,.22);
 }
}

Теперь дочерние элементы следуют друг за другом с небольшой задержкой.

Ответ 7

Мы можем решить это, используя крючки:

Сначала нам понадобится тайм-аут для задержки.

Это вдохновлено хуком useInterval Дэна Абрамова (подробное объяснение см. В блоге Дэна), отличия которого заключаются в следующем:

  1. мы используем мы setTimeout не setInterval
  2. мы возвращаем функцию reset позволяющую перезапустить таймер в любое время

import { useEffect, useRef, useCallback } from 'react';

const useTimeout = (callback, delay) => {
  // save id in a ref
  const timeoutId = useRef('');

  // save callback as a ref so we can update the timeout callback without resetting the clock
  const savedCallback = useRef();
  useEffect(
    () => {
      savedCallback.current = callback;
    },
    [callback],
  );

  // clear the timeout and start a new one, updating the timeoutId ref
  const reset = useCallback(
    () => {
      clearTimeout(timeoutId.current);

      const id = setTimeout(savedCallback.current, delay);
      timeoutId.current = id;
    },
    [delay],
  );

  useEffect(
    () => {
      if (delay !== null) {
        reset();

        return () => clearTimeout(timeoutId.current);
      }
    },
    [delay, reset],
  );

  return { reset };
};