useReducer 与 useContext 一起传递,但子组件显示为空 state.map

useReducer passed with useContext but a child component displays empty state.map

首先感谢大家的支持

作为 ReactJS 世界的新手,我正在尝试完成一个带有一些过滤器作为复选框的产品商店的概念示例。这个想法是,你 select 一个过滤器,你会显示具有 selected 属性的产品。

一切正常,除了当您刷新页面时,您会看到过滤器列和应该显示产品的空白列,即使 console.log(state) 返回正确的对象数组也是如此。

当您单击复选框(过滤器)时,它会正确呈现并显示产品。

GITHUB LINK为完整code

这里是刷新时不显示的组件CardProduct。

import React, { useContext, useEffect } from 'react'
import { AppContext }                   from '../../App'
import { Hearty }                       from '../Hearty'
import Star                             from '../Star'
import boiler                           from '../../images/boiler.png'
import Confronta                        from '../Confronta'

const CardProduct = ({ count, setCount }) => {

    const [state, dispatch] = useContext(AppContext)
    console.log('State in CardProduct:', state)

    function returnCardProduct () {
            return (
                state.map((item, i) => {
                    const { brand, descrizione, prezzo, note, stelle } = item
                    return (
                        <div className="row">
                            <div className="colcard">
                                <div key={ i } className="card"
                                     style={ { width: 'auto', height: 'auto' } }>
                                    <Hearty/>
                                    <img className="card-img-top" src={ boiler } alt="boiler"/>
                                    <div className="card-body">
                                        <p className="card-title"> { brand.toUpperCase() }</p>
                                        <h6 className="card-text">{ descrizione }</h6>
                                        <Star stelle={ stelle }/>
                                        <h4> { prezzo }  </h4>
                                        <h5> { note }  </h5>
                                        <Confronta count={ count } setCount={ setCount }/>
                                    </div>
                                </div>
                            </div>
                        </div>
                    )
                }))
    }

    return (

        <div className="container">
            { returnCardProduct() }
        </div>

    )
}
export default CardProduct

这里是过滤器组件

import { useContext, useEffect, useState } from 'react'
import { AppContext }                      from '../App'


const Filters = () => {

    const [stock, setStock] = useState([])
    const [state,dispatch] = useContext(AppContext)

    function fetchInitialStock () {
        async function fetchStock () {
            let result1 = await fetch('http://localhost:9000/stock').
                then(result1 => result1.json()).
                then(data => setStock(data))
        }
        fetchStock()
        return stock
    }
     useEffect (()=>fetchInitialStock(),[])

console.log( 'initStock' ,stock)
    return (
        <>
            <div className="container">
                <div className="row">

                    <div className="categories">
                        <p>CATEGORIE</p>
                        <h6>Riscaldamento</h6>
                        <h6>Casa e acqua</h6>
                        <h6>Casa</h6>
                        <h6>Acqua</h6>
                    </div>

                    <div className="scegli">
                        <p>SCEGLI PER</p>

                        <h6><span><input type="checkbox"
                                         name="DISPONIBILI"
                                         onChange={(e)=> {
                                              e.target.checked ? dispatch({ type: 'DISPONIBILI' }) : dispatch({ type:'PREV' })



                                         } }/>
                        </span> Disponibili ({stock.map((item) => item.disponibili  )}) </h6>

                        <h6><span><input type="checkbox"
                                         name="PROMO"
                                         onChange={(e)=> e.target.checked ? dispatch({ type: 'PROMO' }) : dispatch({ type: 'PREV' }) }
                        /> </span>In Promozione ({ stock.map((item) => item.inSconto) }) </h6><br/>

                    </div>

                    <div className="marche">
                        <p>MARCHE</p>

                        <h6><span><input type="checkbox" name="ariston" onChange={(e)=>{
                            e.target.checked
                                ? dispatch({ type: 'ARISTON' })
                                : dispatch({ type: 'PREV' })
                        }}
                        /> </span> Ariston ({stock.map((item)=>item.hasOwnProperty('brand')? item.brand.ariston: null)})</h6>

                        <h6><span><input type="checkbox" name="baxi" onChange={(e)=>{
                            e.target.checked
                                ? dispatch({ type: 'BAXI' })
                                : dispatch({ type: 'PREV' })
                        }}/> </span>Baxi ({stock.map((item)=>item.hasOwnProperty('brand')? item.brand.baxi : null)})</h6><br/>
                    </div>

                    <div className="tipologia">
                        <p>TIPOLOGIA</p>

                        <h6><span><input type="checkbox" name="condensazione" onChange={(e)=>{
                            e.target.checked
                                ? dispatch({ type: 'CONDENSAZIONE' })
                                : dispatch({ type: 'PREV' })
                        }}/> </span> A Condensazione ({stock.map((item)=>item.hasOwnProperty('tipologia')? item.tipologia.condensazione: null)}) </h6>

                        <h6><span><input type="checkbox" name="cameraAperta" onChange={(e)=>{
                            e.target.checked
                                ? dispatch({ type: 'APERTA' })
                                : dispatch({ type: 'PREV' })
                        }}/> </span>Camera Aperta ({ stock.map((item)=>item.hasOwnProperty('tipologia')? item.tipologia.cameraAperta: null) }) </h6>

                        <h6><span><input type="checkbox" name="cameraStagna" onChange={(e)=>{
                            e.target.checked
                                ? dispatch({ type: 'STAGNA' })
                                : dispatch({ type: 'PREV' })
                        }}/> </span>Camera Stagna ({ stock.map((item)=>item.hasOwnProperty('tipologia')? item.tipologia.cameraStagna: null) })</h6><br/>
                    </div>


                </div>
            </div>

        </>
    )
}

export default Filters

..最后是 App()

import CardProduct                                        from './components/CardProduct'
import { createContext, useReducer, useState, useEffect } from 'react'
import Filters  from './components/Filters'
import Footer from './components/Footer/Footer'

export const AppContext = createContext()

function App () {

    const [count, setCount] = useState(0)

    function setInit (data, array) {
        data.map((item) => array.push(item))
        return array
    }

    /*Function for setting BOILERS from fetch*/
    function fetchInitialBoiler () {
        let initB = []

        async function fetchBoilers () {
            let response = await fetch('http://localhost:9000/boilers').
                then(response => response.json()).
                then(data => setInit(data, initB))
        }

        fetchBoilers()
        return initB
    }

    const initBoilers = fetchInitialBoiler()
    const [prev, setPrev] = useState([])
    const [state, dispatch] = useReducer(reducer, initBoilers)

    /* Define the reducer function*/
    function reducer (state, action) {
        let current
        switch (action.type) {
            case 'DISPONIBILI':
                current = []
                current = state.filter((item) => item.disponibile ? item : null)
                setPrev(current)
                return current

            case 'PROMO':
                current = []
                current = state.filter((item) => item.inSconto ? item : null)
                setPrev(current)
                return current

            case 'ARISTON':
                current = []
                current = state.filter(
                    (item) => ((item.hasOwnProperty('brand')) &&
                               (item.brand === 'Ariston'))
                        ? item
                        : null)
                setPrev(current)
                return current

            case 'BAXI':
                current = []
                current = state.filter(
                    (item) => (item.hasOwnProperty('brand')) && (item.brand === 'Baxi')
                        ? item
                        : null)
                setPrev(current)
                return current

            case 'CONDENSAZIONE':
                current = []
                current = state.filter((item) => (item.hasOwnProperty('tipologia')) &&
                                                 (item.tipologia === 'condensazione')
                    ? item
                    : null)
                setPrev(current)
                return current

            case 'APERTA':
                current = []
                current = state.filter((item) => (item.hasOwnProperty('tipologia')) &&
                                                 (item.tipologia === 'camera-aperta')
                    ? item
                    : null)
                setPrev(current)
                return current

            case 'STAGNA':
                current = []
                current = state.filter((item) => (item.hasOwnProperty('tipologia')) &&
                                                 (item.tipologia === 'camera-stagna')
                    ? item
                    : null)
                setPrev(current)
                return current

            case 'PREV':
                current = []
                /*console.log('PREV', prev)*/
                return prev
            default:
                return state
        }
    }




    return (
        <>
            <AppContext.Provider value={ [state, dispatch] }>
                <main>
                    <div className="container">

                        <div className="container">
                            <>
                                <div className="col align-self-start">
                                    <Filters/>
                                </div>

                                <div className="col-9">
                                    <CardProduct count={ count } setCount={ setCount }/>
                                </div>
                            </>

                        </div>

                    </div>

                    <>
                        <div>
                            <Footer className="footer" count={ count }/>
                        </div>
                    </>
                </main>
            </AppContext.Provider>
        </>
    )
}

export default App

--- 谢谢 ---

您的 initB 是一个数组声明,它不会重新渲染 React 组件。将其替换为 useState 以查看 API 调用后的更新数据。

const [init, setInit] = useState([]);

其次,

const initBoilers = fetchInitialBoiler()

您应该在安装文档后调用 fetchInitialBoiler(),目前的方法可能是 API 甚至在初始安装之前就被调用。

[useEffect][1] 块中调用它。

useEffect(()=>{
    fetchInitialBoiler() //
    // simply invoke it, since you're not returning anything from the function, there's no need to save the response.
}, [])

您的函数应在 then/catch 块中设置状态,以便组件树在错误情况下重新呈现。

async function fetchBoilers () {
    let response = await fetch('http://localhost:9000/boilers').
        then(response => response.json())
        .then(data => setInit(response))
        .catch(error => setError(error); // state
    }

有关 Fetch 的更多信息 API:https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API

所以最后的问题是将 fetch 作为 initialState 传递给 useReducer。在线搜索我发现它是通过 useEffect 挂钩中的调度操作传递的。在这种情况下,useReducer 钩子在组件内用 initialState '[]' 声明。这里的代码:

  const CardProduct = ({ count, setCount }) => {
    
        const [state, dispatch] = useReducer(reducer, [])
    
        async function initfetch () {
            let response = await fetch('http://localhost:9000/boilers').
                then(response => response.json()).
                then(data => dispatch({
                    type   : 'INITIALIZE',
                    data: data
        }))}
    
        useEffect(() => {
            initfetch()
        }, [])