I am trying to have a button such that when pressed a React context is modified. That is, my context consists of only a list of strings, which is populated when a GET request succeeds. An example of the response is the following:
{
"message": "OK",
"status-code": 200,
"data": {
"models": [
"model1",
"model2",
"model3"
]
}
}
The code regarding the updating of my context is the following:
import React from 'react'
import {
VStack,
Button,
OrderedList,
ListItem
} from '@chakra-ui/react'
const Endpoint = 'http://localhost:5000/'
const ModelsContext = React.createContext([])
function Models() {
const [models, setModels] = React.useContext(ModelsContext)
const updateModels = async () => {
await fetch(Endpoint 'models', {
method: 'GET',
headers: {
Accept: 'application/json'
}
})
.then((resp) => resp.json())
.then(function(data) {
setModels(data.models)
})
}
return (
<VStack>
<Button
size="sm"
colorScheme='teal'
variant='solid'
onClick={updateModels}>
Get Models List
</Button>
<ModelsContext.Provider value={{models}}>
<OrderedList>
{
models.map((name) => (
<ListItem>
{name}
</ListItem>
))
}
</OrderedList>
</ModelsContext.Provider>
</VStack>
)
}
export default Models
I also need to show the results as a Chakra-UI ordered list. However, the models
variable remains undefined inside the <OrderedList />
block, even if I defined a React Provider Context block.
The actal error I get is: "TypeError: models is undefined" in position <OrderedList>
.
Thank you in advance!
CodePudding user response:
You are not using context correctly. Context is for passing values to children without prop drilling. It is great for if you have deeply nested children who rely on the same variables. That way you don't have to pass them down as props you can just grab them by using the useContext
hook. Here is an example of how to use Context:
const Endpoint = 'http://localhost:5000/'
const ModelsContext = createContext([])
function ModelsContextProvider({children}) {
const [models, setModels] = useState([])
return (
<ModelsContext.Provider value={{models, setModels}}>
{children}
</ModelsContext.Provider>
)
}
function Models() {
const { models, setModels } = useContext(ModelsContext)
const updateModels = async () => {
await fetch(Endpoint 'models', {
method: 'GET',
headers: {
Accept: 'application/json'
}
})
.then((resp) => resp.json())
.then(function(data) {
setModels(data.models)
})
}
return (
<VStack>
<Button
size="sm"
colorScheme='teal'
variant='solid'
onClick={updateModels}>
Get Models List
</Button>
<OrderedList>
{
models.map((name) => (
<ListItem>
{name}
</ListItem>
))
}
</OrderedList>
</VStack>
)
}
export default function App() {
return (
<ModelsContextProvider>
<Models />
</ModelsContextProvider>
);
}
In the above example you are setting the context value with the state value. So you can have the useState
hook act as your setter for the context. In your example you don't need context you can just use the useState
hook.
const Endpoint = 'http://localhost:5000/'
function Models() {
const [models, setModels] = React.useState([])
const updateModels = async () => {
await fetch(Endpoint 'models', {
method: 'GET',
headers: {
Accept: 'application/json'
}
})
.then((resp) => resp.json())
.then(function(data) {
setModels(data.models)
})
}
return (
<VStack>
<Button
size="sm"
colorScheme='teal'
variant='solid'
onClick={updateModels}>
Get Models List
</Button>
<OrderedList>
{
models.map((name) => (
<ListItem>
{name}
</ListItem>
))
}
</OrderedList>
</VStack>
)
}
export default Models
CodePudding user response:
You should wrap Models in <ModelsContext.Provider value={{models}}>
, and then inside Models useContext
will return value from Provider.
See example in doc