在函数组件主体之外访问 React Context 值
Posted
技术标签:
【中文标题】在函数组件主体之外访问 React Context 值【英文标题】:Access React Context value outside of the body of a function component 【发布时间】:2021-08-22 03:10:35 【问题描述】:案例
我想让 isLoading
(使用 React Context 的全局状态)值和 changeIsLoading
函数(其从 IsLoadingContext.js 文件中更改的函数)可供所有文件(函数组件和简单的 javascript 函数)访问。
我知道 React Hooks 只能在函数组件的主体内部调用。
问题:在我的例子中,我如何在 util 文件(非函数组件或只是一个简单的 javascript 函数)中调用 isLoading
和 changeIsLoading
?
我应该从代码中更改什么?
代码流程
-
(位置:SummariesPage.js)点击
button
里面的SummariesPage
组件
(位置:SummariesPage.js)在SummariesPage
组件中调用onApplyButtonIsClicked
函数
(位置:SummariesPage.js)将isLoading
全局状态更改为true
,然后调用fetchAPISummaries
函数
(位置:fetchAPISummaries.js)调用fetchAPICycles
函数
(位置:fetchAPICycles.js)调用exportJSONToExcel
函数
(位置:exportJSONToExcel.js)将 JSON 导出到 Excel 文件中,然后将 isLoading
全局状态更改为 false
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
【问题讨论】:
【参考方案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));
;
【讨论】:
以上是关于在函数组件主体之外访问 React Context 值的主要内容,如果未能解决你的问题,请参考以下文章
React.withContext和getChildContext
react-data-components 渲染组件(按钮),但无法访问在渲染方法之外定义的状态或函数
无效的挂钩调用。是不是可以在函数组件的主体之外调用钩子或异步函数?
如何使用 React js 中的上下文将函数从 FUNCTIONAL 传递给 CLASS 组件并在渲染之外(没有 prop)访问它?