TypeError: Cannot read property '' of undefined ı have no idea why ı am getting this error while I do check the code below everything seems fine :( trying to learn the way how react works :) So what is the purpose of this since all the properties I wrap on contextprovider suchas contacts loading and the functions I need
import React, { useState, useContext } from 'react'
import ContactContext from '../context/contactContext'
export default function ContactForm() {
const name = useFormInput('')
const email = useFormInput('')
const contactContext = useContext(ContactContext)
const { addContact } = contactContext
const onSubmit = () => {
addContact(name.value, email.value)
name.onReset()
email.onReset()
}
return (
SOME HTML CODE HERE
)
}
//contactState.js
import React, { useReducer } from 'react'
import _ from 'lodash'
import ContactContext from './contactContext'
import ContactReducer from './contactReducer'
const ContactState = props => {
const initialState = {
contacts: [
{
id: '098',
name: 'Diana Prince',
email: 'diana@us.army.mil'
}
],
loading: false,
error: null
}
const [state, dispatch] = useReducer(ContactReducer, initialState)
const [contacts, loading] = state
const addContact = (name, email) => {
dispatch({
type: 'ADD_CONTACT',
payload: { id: _.uniqueId(10), name, email }
})
}
const delContact = id => {
dispatch({
type: 'DEL_CONTACT',
payload: id
})
}
return (
<ContactContext.Provider
value={{
contacts,
loading,
addContact,
delContact
}}
>
{props.children}
</ContactContext.Provider>
)
}
export default ContactState
//contactReducer.js
export default (state, action) => {
switch (action.type) {
case 'ADD_CONTACT':
return {
contacts: [...state, action.payload]
}
case 'DEL_CONTACT':
return {
contacts: state.contacts.filter(
contact => contact.id !== action.payload
)
}
case 'START':
return {
loading: true
}
case 'COMPLETE':
return {
loading: false
}
default:
throw new Error()
}
}
//contactContext.js
import { createContext } from 'react'
const contactContext = createContext()
export default contactContext
In your reducer, when adding a contact, you're spreading the wrong state key. This should fix it:
case 'ADD_CONTACT':
return {
contacts: [...state.contacts, action.payload]
}
I can't see where you are using ContactState
in your app. If you don't use it and render your ContactForm
component with it then you can't reach any context value. You should render it as:
<ContactState>
<ContactForm />
</ContactState>
in a suitable place in your app. Also, you can't get contacts
and loading
like that:
const [ contacts, loading ] = state;
state
is not an array, it is an object here. You should use:
const { contacts, loading } = state
You can find a simplified version of your code below. I removed/changed some parts in order to run it as much as possible. You should fix your reducer as @Asaf David mentioned in their answer, but this is not the main problem here. After fixing the context issue, you can try to fix your reducer.
About your questions, if you try to understand how React works by looking at this example you can easily get confused. Because Context
is an advanced concept (at least for the beginners). Also, the code uses useReducer
with Context
and this makes the things more complicated. If your intent is to understand the React itself then start with the beginner guide .
By using Context we can pass the data top-down to the deepest components. But, in order to use that data those components should be rendered as children of the context provider.
In your code, you are doing this in ContactState
but you never use it. Also, in that component, you are defining a state with useReducer
and feed your context with this state by value
.
Finally, in your ContactForm
component, you are using useContext hook to get the context data. In your current code since you don't render this component in a provider, contactContext
is undefined and you are getting the error. You can't get addContact
from undefined.
In my example, I'm retrieving the contacts
to show something. Again, I've changed/removed some parts from your code.
const { createContext, useContext, useReducer } = React; const ContactContext = createContext(); function ContactForm() { // Changed those values const name = ""; const email = ""; const contactContext = useContext(ContactContext); // changed addContact -> contacts const { contacts } = contactContext; const onSubmit = () => { addContact(name.value, email.value); name.onReset(); email.onReset(); }; // added some data to render return <div>{contacts[0].name}</div>; } function ContactReducer(state, action) { switch (action.type) { case "ADD_CONTACT": return { contacts: [...state, action.payload] }; case "DEL_CONTACT": return { contacts: state.contacts.filter( contact => contact.id !== action.payload ) }; case "START": return { loading: true }; case "COMPLETE": return { loading: false }; default: throw new Error(); } } const ContactState = props => { const initialState = { contacts: [ { id: "098", name: "Diana Prince", email: "diana@us.army.mil" } ], loading: false, error: null }; const [state, dispatch] = useReducer(ContactReducer, initialState); const { contacts, loading } = state; const addContact = (name, email) => { dispatch({ type: "ADD_CONTACT", // removed lodash part, added a static id payload: { id: 1, name, email } }); }; const delContact = id => { dispatch({ type: "DEL_CONTACT", payload: id }); }; return ( <ContactContext.Provider value={{ contacts, loading, addContact, delContact }} > {props.children} </ContactContext.Provider> ); }; // added the relevant render part ReactDOM.render( <ContactState> <ContactForm /> </ContactState>, document.getElementById("root") );
<script src="https://unpkg.com/react@16/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <div id="root" />
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.