Home > Mobile >  React Context and Provider - TypeError: variable is undefined
React Context and Provider - TypeError: variable is undefined

Time:12-05

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

  • Related