简体   繁体   English

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

[英]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.作为 ReactJS 世界的新手,我正在尝试使用一些过滤器作为复选框来完成一个产品商店的概念示例。 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.一切正常,除了当您刷新页面时,您会得到过滤器列和产品应该出现的空白列,即使console.log(state)返回正确的对象数组。

As you click a checkbox (the filters) it render correctly and the products appear.当您单击复选框(过滤器)时,它会正确呈现并显示产品。

The GITHUB LINK for the complete code .完整codeGITHUB 链接

Here the component CardProduct that does not display at refresh.这里是刷新时不显示的组件 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

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() ..最后是 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 --- - - 谢谢你 - -

Your initB is an array declartion which doesn't re-render a React component.您的initB是一个数组声明,它不会重新渲染 React 组件。 Replace it with useState to see the updated data after the API call.将其替换为useState以查看 API 调用后更新的数据。

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

Secondly,其次,

const initBoilers = fetchInitialBoiler() 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.您应该在安装文档后调用fetchInitialBoiler() ,目前的方法可能是在初始安装之前调用 API。

Invoke it in an [useEffect][1] block.[useEffect][1]块中调用它。

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.您的函数应该在 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
    }

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

So in the end the problem was to pass the fetch as initialState to useReducer.所以最后的问题是将 fetch 作为 initialState 传递给 useReducer。 Searching online I found that is passed throught a dispatch action inside useEffect hook.在线搜索我发现它是通过 useEffect 钩子内的调度操作传递的。 In this case the useReducer hook is declared with initialState '[]' inside the component.在这种情况下, useReducer 钩子在组件内使用 initialState '[]' 声明。 Here the code:这里的代码:

  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()
        }, [])

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM