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

First, I would thank you for the support.

As new to the ReactJS world, I am trying to complete a concept example of a product store with some filters as checkbox. The idea is that you select a filter, you get displayed the products that have the selected proprety.

Everything works, except that when you refresh the page you get the filters column and a blank column where products are supposed to appear, even if the console.log(state) give back the correct array of objects.

As you click a checkbox (the filters) it render correctly and the products appear.

The GITHUB LINK for the complete code.

Here the component CardProduct that does not display at refresh.

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

Here the Filters component

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

..and FINALLY the 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

— THANK YOU —

Answer

Your initB is an array declartion which doesn’t re-render a React component. Replace it with useState to see the updated data after the API call.

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

Secondly,

const initBoilers = fetchInitialBoiler()

you should invoke the fetchInitialBoiler() after the document is mounted, with the current approach chances are the API is invoked even before the intial mount.

Invoke it in an [useEffect][1] block.

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

Your function should set the state in the then/catch block, so that the component tree re-renders in an error case.

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

More on Fetch API: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API