【问题标题】:useReducer passed with useContext but a child component displays empty state.mapuseReducer 与 useContext 一起传递,但子组件显示空 state.map
【发布时间】:2021-12-22 07:11:56
【问题描述】:

首先,感谢您的支持。

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

一切正常,除了当您刷新页面时,您会看到过滤器列和应该出现产品的空白列,即使 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

--- 谢谢---

【问题讨论】:

  • 更新 |按照 sid 的建议(谢谢),我在组件内部使用(但我也在 App() 中尝试过)useState 挂钩从 API 中获取(),并在组件 CardProduct 中的 useEffect 挂钩中设置状态。它在第一次渲染,但我找不到将状态 (const [init,setInit]) 传递给 useReducer 钩子的方法。这似乎是错误的,因为 useReducer 做同样的事情。尝试仅在组件内部使用 useReducer,但它不从 fetch() 获取 initialState(在 useEffect 内部或外部)。我还尝试了没有结果的延迟初始化。
  • 主要问题是我无法从组件内部的useReducer钩子中的fetch()中获取initialState。

标签: javascript reactjs react-redux react-hooks jsx


【解决方案1】:

您的 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

【讨论】:

  • 感谢 sid 的建议。我将重构并更新结果。
  • 嘿,成功了吗?
  • 嗨 sid,感谢您的关注。我在 DEV 中找到了一篇文章,其中建议使用调度操作(在我的情况下为 action.data)传递 initialState 并在 useEffectct 中调用它。它奏效了。我让代码在主帖中。 ?
【解决方案2】:

所以最后的问题是将 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()
        }, [])

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-24
    • 2019-12-09
    • 1970-01-01
    • 2020-11-18
    • 1970-01-01
    • 2021-01-26
    • 2020-05-16
    • 1970-01-01
    相关资源
    最近更新 更多