【问题标题】:Access React Context value outside of the body of a function component在函数组件主体之外访问 React Context 值
【发布时间】:2021-08-22 03:10:35
【问题描述】:

案例

我想让 isLoading(使用 React Context 的全局状态)值和 changeIsLoading 函数(其从 IsLoadingContext.js 文件中更改的函数)可供所有文件(函数组件和简单的 javascript 函数)访问。

我知道 React Hooks 只能在函数组件的主体内部调用。

问题:在我的例子中,我如何在 util 文件(非函数组件或只是一个简单的 javascript 函数)中调用 isLoadingchangeIsLoading

我应该从代码中更改什么?

代码流程

  1. (位置:SummariesPage.js)点击button里面的SummariesPage组件
  2. (位置:SummariesPage.js)在SummariesPage组件中调用onApplyButtonIsClicked函数
  3. (位置:SummariesPage.js)将isLoading全局状态更改为true,然后调用fetchAPISummaries函数
  4. (位置:fetchAPISummaries.js)调用fetchAPICycles函数
  5. (位置:fetchAPICycles.js)调用exportJSONToExcel函数
  6. (位置:exportJSONToExcel.js)将 JSON 导出到 Excel 文件中,然后将 isLoading 全局状态更改为 false
  7. IsLoadingContextProvider 组件将被重新渲染,SummariesPage 中的 isLoading 值将是 true

错误日志

Uncaught (in promise) Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app

代码

IsLoadingContext.js:

import React, { useState } from 'react'

const IsLoadingContext = React.createContext()

const IsLoadingContextProvider = (props) => {
    const [isLoading, setIsLoading] = useState(false)

    const changeIsLoading = (inputState) => {
        setIsLoading(inputState)
    }

    return(
        <IsLoadingContext.Provider
            value={{
                isLoading,
                changeIsLoading
            }}
        >
            {props.children}
        </IsLoadingContext.Provider>
    )
}

export { IsLoadingContextProvider, IsLoadingContext }

SummariesPage.js:

import React, { useContext } from 'react'

// CONTEXTS
import { IsLoadingContext } from '../../contexts/IsLoadingContext'

// COMPONENTS
import Button from '@material-ui/core/Button';

// UTILS
import fetchAPISummaries from '../../utils/export/fetchAPISummaries'

const SummariesPage = () => {
    const { isLoading, changeIsLoading } = useContext(IsLoadingContext)

    const onApplyButtonIsClicked = () => {
        changeIsLoading(true)
        fetchAPISummaries(BEGINTIME, ENDTIME)
    }

    console.log('isLoading', isLoading)

    return(
        <Button
            onClick={onApplyButtonIsClicked}
        >
            Apply
        </Button>
    )
}

export default SummariesPage

fetchAPISummaries.js:

// UTILS
import fetchAPICycles from './fetchAPICycles'

const fetchAPISummaries = (inputBeginTime, inputEndTime) => {
    const COMPLETESUMMARIESURL = .....
    
    fetch(COMPLETESUMMARIESURL, {
        method: "GET"
    })
    .then(response => {
        return response.json()
    })
    .then(responseJson => {
        fetchAPICycles(inputBeginTime, inputEndTime, formatResponseJSON(responseJson))
    })
}

const formatResponseJSON = (inputResponseJSON) => {
    const output = inputResponseJSON.map(item => {
        .....
        return {...item}
    })
    return output
}

export default fetchAPISummaries

fetchAPICycles.js

// UTILS
import exportJSONToExcel from './exportJSONToExcel'

const fetchAPICycles = (inputBeginTime, inputEndTime, inputSummariesData) => {
    const COMPLETDEVICETRIPSURL = .....

    fetch(COMPLETDEVICETRIPSURL, {
        method: "GET"
    })
    .then(response => {
        return response.json()
    })
    .then(responseJson => {
        exportJSONToExcel(inputSummariesData, formatResponseJSON(responseJson))
    })
}

const formatResponseJSON = (inputResponseJSON) => {
    const output = inputResponseJSON.map(item => {
        .....
        return {...item}
    })
    return output
}

export default fetchAPICycles

exportJSONToExcel.js

import { useContext } from 'react'

import XLSX from 'xlsx'

// CONTEXTS
import { IsLoadingContext } from '../../contexts/IsLoadingContext'

const ExportJSONToExcel = (inputSummariesData, inputCyclesData) => {
    const { changeIsLoading } = useContext(IsLoadingContext)

    const sheetSummariesData = inputSummariesData.map((item, index) => {
        let newItem = {}
        .....
        return {...newItem}
    })

    const sheetSummaries = XLSX.utils.json_to_sheet(sheetSummariesData)
    
    const workBook = XLSX.utils.book_new()

    XLSX.utils.book_append_sheet(workBook, sheetSummaries, 'Summaries')
    
    inputCyclesData.forEach(item => {
        const formattedCycles = item['cycles'].map((cycleItem, index) => {
            .....
            return {...newItem}
        })

        const sheetCycles = XLSX.utils.json_to_sheet(formattedCycles)
        XLSX.utils.book_append_sheet(workBook, sheetCycles, item['deviceName'])
    })

    XLSX.writeFile(workBook, `......xlsx`)

    changeIsLoading(false)
}

export default ExportJSONToExcel

【问题讨论】:

    标签: reactjs react-hooks react-context react-functional-component use-context


    【解决方案1】:

    我相信您面临的真正问题是管理异步调用。如果您使用 async/await 关键字,它的可读性会更高。

        const onApplyButtonIsClicked = async () => {
            changeIsLoading(true)
            await fetchAPISummaries(BEGINTIME, ENDTIME)
            changeIsLoading(false)
        }
    

    您需要重写 fetchAPICycles 以使用 async/await 关键字而不是 Promise。

    const fetchAPICycles = async (
      inputBeginTime,
      inputEndTime,
      inputSummariesData
    ) => {
      const COMPLETDEVICETRIPSURL = ...;
    
      const response = await fetch(COMPLETDEVICETRIPSURL, {
        method: "GET",
      });
      const responseJson = await response.json();
      exportJSONToExcel(inputSummariesData, formatResponseJSON(responseJson));
    };
    
    

    【讨论】:

      猜你喜欢
      • 2018-09-23
      • 1970-01-01
      • 1970-01-01
      • 2019-07-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多