javascript - React 在启动时不显示 UI 的初始状态

标签 javascript reactjs sorting react-hooks

我正在使用 React 构建一个电子商务应用程序,我偶然发现了一个问题,即 React 在首次启动页面时不会根据初始状态呈现 UI。
问题描述:

  • 我有一个 sort初始状态为 "latest" 的状态,基于此排序功能 - 如果 sort值为 "latest" - 它将首先排序并返回最新的项目。
  • 但是在开始或刷新页面时,默认值和排序状态仍将是“最新”,但 UI 只会首先显示最旧的项目。
  • 我必须单击其他选项,然后再次选择“最新”选项才能通过排序逻辑。当我刷新页面时,问题又回来了。
  • 对其他值进行排序的逻辑工作正常。在下面的演示中,您可以看到我注销了 sort当前状态。开始时,sort值已经是 "latest" .
  • 在 API product.js文件,我已经按字段 createdAt - 1 对带有 mongoose 的项目进行了排序但似乎它不适用于用户界面?

  • -> 什么情况会导致 React 不根据初始状态渲染项目,我们该如何解决?
    下面是我的代码:
    产品列表.jsx
    const ProductList = () => {
      const location = useLocation()
      const category = location.pathname.split("/")[2]
      const [filters, setFilters] = useState({});
      const [sort, setSort] = useState("latest");
    
      // For Filters Bar
      const handleFilters = (e) => {
        const value = e.target.value
        // When choosing default value, show all products:
        if (value === "") {
          return setFilters([])
        } else {
          setFilters({
            ...filters,
            [e.target.name]: value,
          })
        }
      }
    
      return (
        <Container>
          <Navbar />
          <Announcement />
          <Title>Dresses</Title>
          <FilterContainer>
            <Filter>
              <FilterText>Filter Products: </FilterText>
              <Select name="color" onChange={handleFilters}>
                <Option value="">All Color</Option>
                <Option value="white">White</Option>
                <Option value="black">Black</Option>
                <Option value="brown">Brown</Option>
                <Option value="red">Red</Option>
                <Option value="blue">Blue</Option>
                <Option value="yellow">Yellow</Option>
                <Option value="green">Green</Option>
              </Select>
              <Select name="size" onChange={handleFilters}>
                <Option value="">All Size</Option>
                <Option>XS</Option>
                <Option>S</Option>
                <Option>M</Option>
                <Option>L</Option>
                <Option>XL</Option>
                <Option>36</Option>
                <Option>37</Option>
                <Option>38</Option>
                <Option>39</Option>
                <Option>40</Option>
                <Option>41</Option>
                <Option>42</Option>
                <Option>43</Option>
              </Select>
            </Filter>
            <Filter>
              <FilterText>Sort Products: </FilterText>
              <Select onChange={e => setSort(e.target.value)}>
                <Option value="latest">Latest</Option>
                <Option value="oldest">Oldest</Option>
                <Option value="asc">Price ↑ (Low to High)</Option>
                <Option value="desc">Price ↓ (High to Low)</Option>
              </Select>
            </Filter>
          </FilterContainer>
          <Products category={category} filters={filters} sort={sort} />
          <Newsletter />
          <Footer />
        </Container>
      );
    }
    
    Products.jsx
    const Products = ({ category, filters, sort }) => {
    
      const [products, setProducts] = useState([])
      const [filteredProducts, setFilteredProducts] = useState([])
    
      useEffect(() => {
        const getProducts = async () => {
          try {
            const res = await axios.get( category 
              ? `http://localhost:5000/api/products?category=${category}` 
              : `http://localhost:5000/api/products`
            )
    
            setProducts(res.data)
          } catch (err) {
            console.log(`Fetch all items failed - ${err}`)
          }
        }
        getProducts()
      }, [category])
    
      useEffect(() => {
        category && setFilteredProducts(
          products.filter(item => 
            Object.entries(filters).every(([key, value]) => 
              item[key].includes(value)
            )
          )
        )
      }, [category, filters, products])
    
      // Sorting:
      useEffect(() => {
        console.log(sort)
        if (sort === "latest") {
          setFilteredProducts(prev =>
            [...prev].sort((a, b) => b.createdAt.localeCompare(a.createdAt))
          )
        } else if (sort === "asc") {
          setFilteredProducts(prev =>
            [...prev].sort((a, b) => a.price - b.price)
          )
        } else if (sort === "desc") {
          setFilteredProducts(prev =>
            [...prev].sort((a, b) => b.price - a.price)
          )
        } else {
          setFilteredProducts(prev =>
            [...prev].sort((a, b) => a.createdAt.localeCompare(b.createdAt))
          )
        }
      }, [sort])
    
      return (
        <Container>
          <Title>Popular In Store</Title>
          <ProductsWrapper>
            {filteredProducts.map(item => (
            <Product key={item._id} item={item} />
          ))}
          </ProductsWrapper>
        </Container>
      );
    }
    
    API 路由 - product.js
    const router = require('express').Router()
    const { verifyTokenAndAdmin } = require('./verifyToken')
    const Product = require('../models/Product')
    
    // .... (Other CRUD)
    
    // GET ALL PRODUCTS
    router.get("/", async(req, res) => {
      const queryNew = req.query.new
      const queryCategory = req.query.category
    
      try {
        let products = []
        
        if(queryNew) {
          products = await Product.find().sort({ createdAt: -1 }).limit(5)
        } else if (queryCategory) {
          products = await Product.find({ 
            categories: {
              $in: [queryCategory],
            }, 
          })
        } else {
          products = await Product.find()
        }
    
        res.status(200).json(products)
      } catch(err) {
        res.status(500).json(`Cannot fetch all products - ${err}`)
      }
    })
    
    module.exports = router
    
    演示:
    demo gif
  • 解释演示:在开始时,它首先呈现最旧的项目。必须选择另一个选项,然后返回到最新选项进行渲染。但在控制台中,排序的初始状态已经是“最新”但它与 useEffect 排序逻辑不匹配。

  • 更新
    根据 @idembele70's answer ,我将过滤器的初始状态输入错误为 Array。
  • 我已修复它并添加了 name="sort"在排序选择上。
  • 我也替换了value="latest"defaultValue="latest"对于我的排序选择栏 . -> 这使得 最新选项 停止工作,所以我认为它不能在这种情况下使用?
  • 结果还是一样,UI 没有渲染排序栏的逻辑来优先显示最新的项目。

  • 代码
    const ProductList = () => {
      const location = useLocation()
      const category = location.pathname.split("/")[2]
      const [filters, setFilters] = useState({});
      const [sort, setSort] = useState("latest");
    
      const handleFilters = (e) => {
        const value = e.target.value
        // When choosing default value, show all products:
        if (value === "") {
          setFilters({}) // Changed from array to object
        } else {
          setFilters({
            ...filters,
            [e.target.name]: value,
          })
        }
      }
    
    ...
     <Filter>
      <FilterText>Sort Products: </FilterText>
      <Select name="sort" onChange={e => setSort(e.target.value)} >
        <Option defaultValue="latest">Latest</Option>
        <Option value="oldest">Oldest</Option>
        <Option value="asc">Price ↑ (Low to High)</Option>
        <Option value="desc">Price ↓ (High to Low)</Option>
      </Select>
    </Filter>
    

    最佳答案

    你不应该把改变的products到状态,因为它使保持更新变得更加复杂,您需要处理各种useEffect案例。相反,最好定义排序和过滤函数并在渲染时应用它们。这样,您将确保渲染结果始终与数据保持同步:

    const filterProducts = (products) => {
      if (!category) {
        return products;
      }
      return products.filter(item =>
        Object.entries(filters).every(([key, value]) =>
          item[key].includes(value),
        ),
      );
    };
    
    const sortProducts = (products) => {
      switch (sort) {
        case "asc":
          return [...products].sort((a, b) => a.price - b.price);
        case "desc":
          return [...products].sort((a, b) => b.price - a.price);
        case "latest":
        default:
          return [...products].sort((a, b) => a.createdAt.localeCompare(b.createdAt));
      }
    };
    
    return (
      <Container>
        <Title>Popular In Store</Title>
        <ProductsWrapper>
          {sortProducts(filterProducts(products)).map(item => (
            <Product key={item._id} item={item} />
          ))}
        </ProductsWrapper>
      </Container>
    );
    
    

    关于javascript - React 在启动时不显示 UI 的初始状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71793518/

    相关文章:

    javascript - Firebase:TypeError:fbRef.child 不是函数

    javascript - ReactJs - 为什么子事件不会改变父事件的状态?

    javascript - HTML + react : Radio button stuck on the one initially set as "checked"

    reactjs - 带 React 路由器的 Firebase 托管

    sorting - 使用有序数组对字典进行排序

    java - 为什么我的排序函数会产生如此不寻常的输出?

    javascript - 如何将字符串中的数字分成四个字符 block

    javascript - 解析: Retrieving properties from an object that is related

    reactjs - 为什么 MobX v6.x 在 React with Typescript 中不能按预期工作?

    python - 按值对字典进行排序,其中值是字典