Vertraagde weergave van React-componenten

Ik heb een React-component met een aantal onderliggende componenten erin. Ik wil de onderliggende componenten niet in één keer maar met enige vertraging weergeven (uniform of verschillend voor elk van de kinderen).

Ik vroeg me af: is er een manier om dit te doen?


Antwoord 1, autoriteit 100%

Ik denk dat de meest intuïtieve manier om dit te doen is door de kinderen een “wacht” propte geven, die het onderdeel verbergt voor de duur die werd doorgegeven door de ouder. Door de standaardstatus op verborgen te zetten, zal React het onderdeel nog steeds onmiddellijk weergeven, maar het is pas zichtbaar als de status is gewijzigd. Vervolgens kun je componentWillMountinstellen om een ​​functie aan te roepen om deze te laten zien na de duur die is doorgegeven via rekwisieten.

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>
        )
    }
});

Vervolgens, in de Ouder-component, hoeft u alleen maar de tijd door te geven die u wilt dat een kind moet wachten voordat het wordt weergegeven.

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>
        )
    }
});

Hier is een demo


Antwoord 2, autoriteit 56%

Een andere benadering voor een vertraagd onderdeel:

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;

Gebruik:

import Delayed from '../Time/Delayed';
 import React from 'react';
 const myComp = props => (
     <Delayed waitBeforeShow={500}>
         <div>Some child</div>
     </Delayed>
 )

Antwoord 3, autoriteit 40%

Ik heb de vertraagde component gemaakt met behulp van Hooks en TypeScript

import React, { useState, useEffect } from 'react';
type Props = {
  children: React.ReactNode;
  waitBeforeShow?: number;
};
const Delayed = ({ children, waitBeforeShow = 500 }: Props) => {
  const [isShown, setIsShown] = useState(false);
  useEffect(() => {
    setTimeout(() => {
      setIsShown(true);
    }, waitBeforeShow);
  }, [waitBeforeShow]);
  return isShown ? children : null;
};
export default Delayed;

Wikkel gewoon een ander onderdeel in Delayed

export const LoadingScreen = () => {
  return (
    <Delayed>
      <div />
    </Delayed>
  );
};

Antwoord 4, autoriteit 21%

In uw vadercomponent <Father />kunt u een beginstatus creëren waarin u elk kind volgt (bijvoorbeeld met en id), door een booleaanse waarde toe te kennen, wat betekent dat u al dan niet rendert :

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

Vervolgens, wanneer het onderdeel is gemonteerd, start u uw timers om de status te wijzigen:

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

Als je je kinderen rendert, doe je dat door ze opnieuw te maken, waarbij je als prop de staat toewijst voor het overeenkomende kind dat zegt of het onderdeel moet worden weergegeven of niet.

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

Dus in uw <Child />component

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

Wanneer de time-out wordt geactiveerd, wordt de status gewijzigd en wordt de vadercomponent opnieuw weergegeven. De rekwisieten voor kinderen zijn bijgewerkt en als doRendertrueis, zullen ze zichzelf weergeven.


Antwoord 5, autoriteit 6%

Met behulp van de useEffect hook kunnen we de vertragingsfunctie gemakkelijk implementeren tijdens het typen in het invoerveld:

import React, { useState, useEffect } from 'react'
function Search() {
  const [searchTerm, setSearchTerm] = useState('')
  // Without delay
  // useEffect(() => {
  //   console.log(searchTerm)
  // }, [searchTerm])
  // With delay
  useEffect(() => {
    const delayDebounceFn = setTimeout(() => {
      console.log(searchTerm)
      // Send Axios request here
    }, 3000)
    // Cleanup fn
    return () => clearTimeout(delayDebounceFn)
  }, [searchTerm])
  return (
    <input
      autoFocus
      type='text'
      autoComplete='off'
      className='live-search-field'
      placeholder='Search here...'
      onChange={(e) => setSearchTerm(e.target.value)}
    />
  )
}
export default Search

Antwoord 6, autoriteit 5%

Afhankelijk van uw gebruik.

Als je wat animatie wilt maken van kinderen die opgaan in het geheel, gebruik dan de reactie-animatie-add-on: https://facebook.github.io/react/docs/animation.html
Maak anders de weergave van de kinderen afhankelijk van rekwisieten en voeg de rekwisieten na enige vertraging toe.

Ik zou het onderdeel niet uitstellen, omdat het je waarschijnlijk zal achtervolgen tijdens het testen. En idealiter zouden componenten puur moeten zijn.


Antwoord 7, autoriteit 5%

We kunnen dit oplossen met Hooks:

Eerst hebben we een time-out nodig voor de vertraging.

Deze is geïnspireerd op Dan Abramovs useInterval hook (zie Dan’s blogbericht voor een uitgebreide uitleg), de verschillen zijn:

  1. we gebruiken we setTimeout niet setInterval
  2. we retourneren een reset-functie waarmee we de timer op elk moment opnieuw kunnen starten
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 };
};

Snippet uitvouwen


Antwoord 8, autoriteit 2%

Mijn gebruiksscenario is misschien een beetje anders, maar ik dacht dat het misschien handig zou zijn om een ​​oplossing te posten die ik heb bedacht, omdat er een andere benadering voor nodig is.

In wezen heb ik een Popover-component van een derde partij die een anker-DOM-element als een prop gebruikt. Het probleem is dat ik niet kan garanderen dat het ankerelement er onmiddellijk zal zijn, omdat het ankerelement zichtbaar wordt op hetzelfde moment als de Popover die ik eraan wil verankeren (tijdens dezelfde redux-verzending).

Een mogelijke oplossing was om het Popover-element dieper in de componentenboom te plaatsen dan het element dat het ook moest worden verankerd. Dat paste echter niet goed bij de logische opbouw van mijn componenten.

Uiteindelijk heb ik besloten om de (re)render van de Popover-component een beetje uit te stellen om ervoor te zorgen dat het anker DOM-element kan worden gevonden. Het gebruikt de functie als een kindpatroon om de kinderen pas na een vaste vertraging weer te geven:

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
}

Het kan als volgt worden gebruikt:

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

Voelt mij nogal hacky uit, maar werkt toch voor mijn gebruik.


Antwoord 9, autoriteit 2%

render de onderliggende componenten niet in één keer maar met enige vertraging.

De vraag zegt vertraging renderen maar als het goed is om te renderen maar te verbergen…

Je kunt de componenten meteen van een kaart renderen, maar gebruik CSS-animatie om de weergave ervan te vertragen.

@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);
 }
}

De onderliggende divs volgen elkaar nu met een kleine vertraging op.


Antwoord 10, autoriteit 2%

Ik heb er hier nog een met een terugvaloptie zoals Suspense

import { useState, useEffect } from "react";
export default function FakeSuspense(props) {
  const { children, delay, fallback } = props;
  const [isShown, setIsShown] = useState(false);
  useEffect(() => {
    setTimeout(() => {
      setIsShown(true);
    }, delay);
  }, [delay]);
  return isShown ? children : fallback;
}

gebruik het dan

<FakeSuspense delay={1700} fallback={<Spinner />}>
  <Component />
</FakeSuspense>

Antwoord 11

Zeer mooie lus die ik gebruik. Een groot pluspunt van deze functie is dat het zichzelf niet herhaalt wanneer het niet meer nodig is.

function geniusLoop()
{
    console.log("TRYING TO LOOP")
    if (iFinishedMyTask == false)
    {
        setTimeout(function () { geniusLoop(); }, 1000);
        console.log("I LOOPED")
        DoSomething();
    }
}

Other episodes