Индикатор загрузки, который отображается, когда все асинхронные действия выполнены, и имеет минимальное время отображения для лучшего UX - PullRequest
0 голосов
/ 02 декабря 2018

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

Например, приведенная ниже опора isLoading вернет объект, содержащий вседействия приложения и их текущее состояние загрузки, например:

isLoading: {
    SOME_ACTION_FIRST: false,
    SOME_ACTION_SECOND: true,
    SOME_ACTION_THIRD: false
}

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

class ExampleComponentUsingLoader extends Component {

    checkForallFalseValues = (obj) => {
      return Object.keys(obj).every(function(val){ 
        return obj[val] === false 
      })
    }

    render(){
        const { isLoading } = this.props
        return(
            { !allFalseValues(loading) && <LoadingIndicator /> }
        )
    }
}

Там, где все становится сложнее - индикация минимального времени загрузки

Опыт пользователя в React - это благословение ипроклятие.С одной стороны, это действительно быстро, и это может показаться волшебным.С другой стороны, это может быть слишком быстро, и это действительно очень неприятно.

Из сообщения об обмене стека UX обычно считается, что 1 секунда - это идеальная длина для потока пользователя:

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

https://ux.stackexchange.com/questions/104606/should-a-loading-text-or-spinner-stay-a-minimum-time-on-screen#answer-104782

Поэтому моя цель состоит не только в том, чтобы отображать индикатор в зависимости от состояния загрузки действий , но и длятакже укажите минимальное время отображения .

Что необходимо учесть:

  1. В некоторых случаях действия будут длиться более 1 секунды, и в этом случае я толькохотите отобразить индикатор за это время.
  2. В других случаях действия будут занимать менее 1 секунды, и в этом случае мне нужно убедиться, что индикатор отображается не менее 1 секунды.

То, что я пробовал

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

class LoadingIndicator extends React.Component {
  constructor(props) {
    super(props);
    this.enableMessage = this.enableMessage.bind(this);

    this.state = {
      displayMessage: false,
    };

    this.timer = setTimeout(this.enableMessage, 250);
  }

  componentWillUnmount() {
    clearTimeout(this.timer);
  }

  enableMessage() {
    this.setState({displayMessage: true});
  }

  render() {
    const {displayMessage} = this.state;

    if (!displayMessage) {
      return null;
    }

    return <div>Loading...</div>;
  }
}

Я ищу несколько советов о том, как наилучшим образом объединить идею такого типа с фактическим временем загрузки из пропеллера действий isLoading.

1 Ответ

0 голосов
/ 02 декабря 2018

Я начал придумывать довольно сложное решение, а потом понял, что есть гораздо более простое.Оба используют обещания.У меня нет большого опыта работы с React или Promises, но я думаю, что нечто подобное должно приблизить вас.Сначала я приведу простой:

class ExampleComponentUsingLoader extends Component {

  checkForallFalseValues = (obj) => {
    return Object.keys(obj).every(function(val){ 
      return obj[val] === false 
    })
  }

  constructor(props) {
    super(props)
    this.delayedPromise = new Promise((resolve, reject) => setTimeout(() => resolve(), 1000))

    this.state = { loading: true, delayMet: false }
    this.delayedPromise.then(() => this.setState({ delayMet: true }))
  }

  render(){
    const { isLoading } = this.props
    if (checkForAllFalseValues(isLoading)) {
      this.state.setState({ loading: false });
    }
    return(
        { !(this.state.delayMet && !this.state.loading) && <LoadingIndicator /> }
    )
  }
}

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

function DelayedPromise(delay) {
  const self = this;
  
  // This is what takes care of the timing, it will resolve after the delay
  const timerPromise = new Promise(function(resolve, reject) {
    setTimeout(function() { resolve(); }, delay);
  });
  
  const resolvablePromise = new Promise(function(resolve, reject) {
    self.resolve = resolve;
    self.reject = reject;
  });

  // This gives you the promise functionality.
  const proxyPromise = Promise.all([timerPromise, resolvablePromise]);
  
  proxyPromise.resolve = self.resolve;
  proxyPromise.reject = self.reject;
  
  return proxyPromise;
}

console.log('start', new Date());
const p = new DelayedPromise(1000);
p.then(function() { console.log('resolved', new Date()); });
p.resolve();

А вот возможность его использования:

class ExampleComponentUsingLoader extends Component {

  checkForallFalseValues = (obj) => {
    return Object.keys(obj).every(function(val){ 
      return obj[val] === false 
    })
  }

  constructor(props) {
    super(props)
    this.delayedPromise = new DelayedPromise(1000)

    this.state = { loading: true }
    this.delayedPromise.then(() => this.setState({ loading: false }))
  }

  render(){
    const { isLoading } = this.props
    if (checkForAllFalseValues(isLoading)) {
      this.delayedPromise.resolve();
    }
    return(
        { this.state.loading && <LoadingIndicator /> }
    )
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...