You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React组件间布尔类型Props传递及骨架屏实现问题

React组件骨架屏实现及布尔Props传递修复

问题定位

  1. AllPosts.jsx:未将维护的isPostsLoading状态传递给子组件Posts,导致后续组件无法获取全局加载状态
  2. Posts.jsx:渲染过滤后的帖子列表时,未给Post组件传递isPostsLoading属性;同时全局加载状态和搜索加载状态的逻辑边界不清晰
  3. Post.jsx:缺失moment和Link的导入;加载状态下返回的骨架结构与实际帖子布局不匹配,且批量生成骨架的逻辑不符合"每个未加载div显示骨架"的需求

修复方案

1. 修改AllPosts.jsx:传递加载状态props

将isPostsLoading作为props传递给Posts组件:

export default function AllPosts() {
  const [posts, setPosts] = useState([]);
  const { search } = useLocation();
  const [isPostsLoading, setIsPostsLoading] = useState(true);

  useEffect(() => {
    const fetchPosts = async () => {
      const res = await axios.get("/posts" + search);
      setPosts(res.data);
      setIsPostsLoading(false);
    };
    fetchPosts();
  }, [search]);

  return (
    <>
      <div className="posts">
        <div className="header">
          <div className="headerTitle">
            <span className="headerTitleLg">المقالات</span>
          </div>
        </div>
        <div className="postsContent">
          {/* 传递isPostsLoading给Posts组件 */}
          <Posts posts={posts} isPostsLoading={isPostsLoading} />
        </div>
      </div>
    </>
  );
}

2. 修改Posts.jsx:统一传递加载状态,优化加载逻辑

在渲染过滤结果时也传递isPostsLoading,并调整全局加载状态的显示位置:

import SearchIcon from '@mui/icons-material/Search';
import CircularProgress from '@mui/material/CircularProgress';
import Post from './Post'; // 确保导入Post组件
import { useState } from 'react';
import Box from '@mui/material/Box';

export default function Posts({ posts = [], isPostsLoading}) {
    const [filteredResults, setFilteredResults] = useState([]);
    const [searchInput, setSearchInput] = useState('');
    const [isSearchLoading, setIsSearchLoading] = useState(false); // 重命名区分搜索加载状态

    const filter = (e) => {
      const keyword = e.target.value;
      setIsSearchLoading(true);
      setTimeout(() => { // 模拟搜索延迟,实际场景可移除
        if (keyword !== '') {
            const filteredData = posts.filter((post) => {
                return Object.values(post)
                .join('')
                .toLowerCase()
                .includes(keyword.toLowerCase()) // 修正搜索条件使用当前输入值
            })
            setFilteredResults(filteredData)
        } else {
          setFilteredResults(posts)
        }
        setSearchInput(keyword);
        setIsSearchLoading(false);
      }, 300);
    }

    // 全局加载时直接显示骨架屏列表
    if (isPostsLoading) {
      return (
        <div className="postsBox">
          {Array.from({length: 6}).map((_, idx) => (
            <Post key={idx} isPostsLoading={true} />
          ))}
        </div>
      )
    }

  return (
    <div className="posts">
      <div className="postsContainer">
        <div className="postsSearchContainer">
          <div className="postsSearch">
            <div className="postsSearchIconContainer">
              <SearchIcon className="w-5 h-5" />
            </div>
            <input 
              type="text"
              className="postsSearchInput"
              placeholder="search"
              name="postsSearchText"
              id="postsSearchText"
              onChange={filter}
            />
          </div>
        </div>
        <div className="postsBoxContainer">
          <div className="postsBox">
            {isSearchLoading ? (
              <Box sx={{ display: 'flex', justifyContent: 'center', padding: '2rem' }}>
                <CircularProgress />
              </Box>
            ) : (
              filteredResults.length > 0 ? (
                filteredResults.map((filteredPosts) => (
                  <Post key={filteredPosts._id} post={filteredPosts} isPostsLoading={false} />
                ))
              ) : (
                posts.map((post) => (
                  <Post key={post._id} post={post} isPostsLoading={false} />
                ))
              )
            )}
          </div>
        </div>
      </div>
    </div>
  );
};

3. 修改Post.jsx:补全依赖,匹配布局的骨架屏

补全缺失的导入,调整骨架结构与实际帖子布局一致,实现单个帖子的骨架效果:

import Skeleton, { SkeletonTheme } from 'react-loading-skeleton'
import 'react-loading-skeleton/dist/skeleton.css'
import moment from 'moment'; // 补全moment导入
import { Link } from 'react-router-dom'; // 补全Link导入

export default function Post({ post, isPostsLoading }) {
  // 加载状态下返回单个帖子的骨架
  if (isPostsLoading) {
    return (
      <SkeletonTheme color='#F5F5F5' highlightColor='#ffffff'>
        <div className="post">
          <div className="postImg">
            <Skeleton width='100%' height={200} />
          </div>
          <div className="postInfo">
            <span className="postTitle">
              <Skeleton height={30} width='80%' />
            </span>
            <div>
              <Skeleton count={3} height={16} className="postDesc" />
            </div>
            <span className="postDate">
              <Skeleton width={100} height={16} />
            </span>
          </div>
        </div>
      </SkeletonTheme>
    )
  }

  // 非加载状态渲染实际内容
  moment.locale('ar');
  const daysAgo = moment(post.createdAt);

  return (
    <div className="post">
      <div>
        {post.photo && (
          <Link to={`/post/${post._id}`} className="link">
            <img
              className="postImg" 
              src={post.photo} 
              alt="" 
            />
          </Link>
        )}
      </div>
      <div className="postInfo">
        <Link to={`/post/${post._id}`} className="link">
          <span className="postTitle">
            {post.title}
          </span>
        </Link>
        <div> 
          <p dir="auto" className="postDesc">
            {post.content}
          </p>
        </div>
        <span className="postDate">
          {daysAgo.fromNow()}
        </span>
      </div>
    </div>
  );
}

关键优化点

  • 明确区分全局数据加载状态(isPostsLoading)和搜索加载状态(isSearchLoading),避免逻辑混淆
  • 单个Post组件的骨架结构与实际帖子布局完全匹配,实现"每个未加载div显示骨架"的需求
  • 补全所有缺失的依赖导入,消除运行时错误
  • 给列表项添加key属性,符合React列表渲染规范

内容的提问来源于stack exchange,提问作者sultan.h

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 17:10:33