Сначала загрузите раздел html перед дорогостоящим разделом

#reactjs

#reactjs

Вопрос:

У меня есть трудоемкий цикл для таблицы, которая выводит около 300 ячеек с некоторыми пользовательскими вычислениями для каждой, загрузка занимает 4-5 секунд, что имеет смысл. Поэтому я хочу отобразить режим загрузки, пока он делает это. Однако модальный отображается только после завершения дорогостоящего цикла?

 const [loading, setLoading] = useState<boolean>(false);

useEffect(() => {
    if(loading){
      console.log(loading, "Loading has started")
    }   
}, [loading])


return(

<>
  <Modal show={loading}>Loading....</Modal>
  
  <a onClick={() => setLoading(true)}>Load more</a>
  
  // Expensive loop
  array.map(() => {
    // HMTL
    <div>
      ----
    </div>
  })
  
</>

) 

Я вижу из журнала консоли, что состояние для «загрузки» было обновлено, но модальное еще не отображено, поэтому я предполагаю, что оно ожидает завершения цикла (чего я не хочу). Есть ли способ заставить его сначала выводить html для модального, или я упускаю фундаментальную часть реакции?

Ответ №1:

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

Я не уверен, что полностью понимаю, как работает загрузочная часть, но что-то вроде этого примера должно помочь:

 const [loading, setLoading] = useState<boolean>(false);
const [computedArray, setComputedArray] = useState([]);

useEffect(() => {
    setComputedArray(array.map(//expensive loop here))
    setLoading(false)
}, [array])

useEffect(() => {
    if(loading){
      console.log(loading, "Loading has started")
    }   
}, [loading])


return(

<>
  <Modal show={loading}>Loading....</Modal>
  
  <a onClick={() => setLoading(true)}>Load more</a>
  
  // Cheap loop, turn data into markup
  computedArray.map(() => {
    // HTML
    <div>
      ----
    </div>
  })
  
</>

) 

Комментарии:

1. Полагаю, мне следовало упомянуть, что внутри дорогостоящего цикла массива есть и другие компоненты. Но вы говорите, что я должен вычислить их все, включая HTML-вывод вне этого рендеринга?

2. @CerIs это зависит от того, насколько легко разделить дорогостоящие части на части, превращающие данные в разметку, я обычно конвертирую их в html при каждом рендеринге, но если это невозможно, вы должны быть в порядке, предварительно вычисляя его, если ваши зависимости от эффекта использования верны

Ответ №2:

Я не знаю, что Modal из этого следует, поэтому отвечаю с общей точки зрения:

Лично я бы сохранил массив в состоянии и обновил его в отдельной функции, а также использовал amp;amp; символ для отображения загружаемого элемента. Таким образом, вы сохраняете всю свою логику относительно дорогостоящего расчета и погрузки / разгрузки в одном месте.

 export default function App() {
  const [loading, setLoading] = useState(false);
  const [array, setArray] = useState([]);

  const getExpensiveCalculation = async () => {
    setLoading(true);
    const resultOfExpensiveCalculation = await ... // something here
    // assuming result is a spreadable array too
    setArray([...array, ...resultOfExpensiveCalculation]);
    setLoading(false);
  };

  return (
    <>
      {loading amp;amp; <Modal>Loading....</Modal>}

      <a onClick={getExpensiveCalculation}>Load more</a>

      {array.map(element => (
        <div>
        ---
        </div>
      ))}
    </>
  );
}