如何避免在 React 中获取数据后 1 秒内不必要的组件闪烁?

问题描述 投票:0回答:2

如图所示,获取数据并将其显示在屏幕上后,橙色按钮出现(中间)1秒,然后消失。

组件代码:

const Home: FC = () => {
    const { pizzas, loading, error, count } = useAppSelector(
        (state) => state.pizzas
    )
    const { categoryID, searchValue, currentPage, sortNameObj } =
        useAppSelector((state) => state.filter)
    const dispatch = useAppDispatch()

    const handleChangeCategory = useCallback((index: number) => {
        dispatch(setCategoryID(index))
        dispatch(setCurrentPage(1))
    }, [])

    const handleChangePage = (page: number) => {
        dispatch(setCurrentPage(page))
    }

    const pizzaList = pizzas?.map((item) => {
        const pizzaImg = pizzaImagesMiddle[item.title]

        return <PizzaCard key={item.id} item={item} pizzaImg={pizzaImg} />
    })

    const skeletons = [...new Array(4)].map((_, index) => (
        <PizzaSkeleton key={index} />
    ))

    const loadedPizzas = loading ? skeletons : pizzaList

    useEffect(() => {
        dispatch(fetchPizzas())
    }, [categoryID, searchValue, sortNameObj, currentPage])

    if (error) {
        return <EmptyResult title='Произошла ошибка' />
    }

    if (!loading && (!pizzas || pizzas?.length === 0)) {
        return <EmptyResult title='Пиццы не найдены' />
    }

    return (
        <div className={styles.home__container}>
            <div className={styles.content__header}>
                <Categories
                    handleChangeCategory={handleChangeCategory}
                    value={categoryID}
                />
                <Sort sortNameObj={sortNameObj} />
            </div>
            <h2>Все пиццы</h2>
            <section className={styles.content__list}>{loadedPizzas}</section>
            <Pagination
                handleChangePage={handleChangePage}
                currentPage={currentPage}
                itemsLength={count}
            />
        </div>
    )
}

这是因为检查披萨的长度是否完好

if (!loading && (!pizzas || pizzas?.length === 0))
。无需检查空长度
if (!loading && !pizzas)
一切顺利。但我需要检查数组是否为空。

默认情况下,披萨长度为空(所以在获取数据之前我有空数组)

披萨片:

const initialState: PizzasState = {
    pizzas: [],
    loading: false,
    error: null,
    count: 0
}

const pizzasSlice = createSlice({
    name: 'pizzas',
    initialState,
    reducers: {},
    extraReducers: (builder) => {
        builder.addCase(fetchPizzas.pending, (state) => {
            state.loading = true;
            state.pizzas = [];
            state.error = null;
            state.count = 0
        });
        builder.addCase(fetchPizzas.fulfilled, (state, action) => {
            state.pizzas = action.payload.items;
            state.error = null;
            state.count = action.payload.count;
            state.loading = false
        });
        builder.addCase(fetchPizzas.rejected, (state, action) => {
            state.pizzas = [];
            state.count = 0;
            if (action.payload) {
                state.error = action.payload.message
            } else {
                state.error = action.error.message
            };
            state.loading = false
        })
    }
})

问题:如何正确避免闪烁

<EmptyResult/>
1秒?

javascript reactjs typescript redux redux-toolkit
2个回答
0
投票

您可以在 useEffect 上放置 setTimeout(() =>dispatch(fetchPizzas()), 1000),或者,如果这些函数中的任何一个是异步的,您可以使用 State 有条件地渲染或不渲染组件。在返回要延迟的组件的地方,请执行以下操作: return shouldRenderPizza ? (你实际的披萨成分):

现在,在你的 useEffect 上做一些像dispatch(fetchPizzas().then(result => setTimeout(() => setShouldRenderPizza(true), 1000)))这样的事情


0
投票

问题

EmptyResult
为 true 并且
loading
状态为 false 或为空时,当前显示
pizzas
组件。
pizzas
状态最初为空,并且当
[]
操作待处理时也设置为
fetchPizzas

解决方案

如果您只想在加载数据后显示

EmptyResult
,请从 [] - “获取数据并清空”和
[....]
- “获取数据并填充”中选择一个
 different
值,以区分状态和加载状况。此处使用
undefined
null
是有效的选择,表示数据尚未获取/加载,并且很容易在 UI 中检查。

const initialState: PizzasState = {
  pizzas: undefined, // <-- initially undefined
  loading: false,
  error: null,
  count: 0,
};

const pizzasSlice = createSlice({
  name: 'pizzas',
  initialState,
  extraReducers: (builder) => {
    builder.addCase(fetchPizzas.pending, (state) => {
      state.loading = true;
      // state.pizzas = []; // <-- Don't update yet
      state.error = null;
      state.count = 0
    });
    builder.addCase(fetchPizzas.fulfilled, (state, action) => {
      state.pizzas = action.payload.items; // <-- update to "loaded" value
      state.error = null;
      state.count = action.payload.count;
      state.loading = false;
    });
    builder.addCase(fetchPizzas.rejected, (state, action) => {
      state.pizzas = []; // <-- update to "loaded" value
      state.count = 0;
      state.error = action.payload
        ? action.payload.message
        : action.error.message;
      state.loading = false;
    });
  },
});

更新 UI 以检查

undefined
/
null
加载的数据。

const skeletons = [...new Array(4)].map((_, index) => (
  <PizzaSkeleton key={index} />
));

const Home: FC = () => {
  const { pizzas, loading, error, count } =
    useAppSelector((state) => state.pizzas);
  const { categoryID, searchValue, currentPage, sortNameObj } =
    useAppSelector((state) => state.filter);
  const dispatch = useAppDispatch();

  const handleChangeCategory = useCallback((index: number) => {
    dispatch(setCategoryID(index));
    dispatch(setCurrentPage(1));
  }, []);

  useEffect(() => {
    dispatch(fetchPizzas())
  }, [categoryID, searchValue, sortNameObj, currentPage])

  const handleChangePage = (page: number) => {
    dispatch(setCurrentPage(page));
  };

  if (error) {
    return <EmptyResult title='Произошла ошибка' />;
  }

  // Check if pizzas is a defined array and empty
  if (!loading && (Array.isArray(pizzas) && !pizzas.length)) {
    return <EmptyResult title='Пиццы не найдены' />;
  }

  const pizzaList = pizzas?.map((item) => (
    <PizzaCard
      key={item.id}
      item={item}
      pizzaImg={pizzaImagesMiddle[item.title]}
    />
  ));

  const loadedPizzas = loading ? skeletons : pizzaList;

  return (
    <div className={styles.home__container}>
      <div className={styles.content__header}>
        <Categories
          handleChangeCategory={handleChangeCategory}
          value={categoryID}
        />
        <Sort sortNameObj={sortNameObj} />
      </div>
      <h2>Все пиццы</h2>
      <section className={styles.content__list}>
        {loadedPizzas}
      </section>
      <Pagination
        handleChangePage={handleChangePage}
        currentPage={currentPage}
        itemsLength={count}
      />
    </div>
  );
};
© www.soinside.com 2019 - 2024. All rights reserved.