TypeError:无法读取未定义的属性”,我不知道为什么我在检查所有下面的代码时会收到此错误:(试图了解反应的方式:) 那么这是什么目的,因为我包装在contextprovider上的所有属性(例如联系人加载和我需要的功能)
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
答案 0 :(得分:0)
在减速器中,添加联系人时,您散布了错误的状态键。这应该解决它:
case 'ADD_CONTACT':
return {
contacts: [...state.contacts, action.payload]
}
答案 1 :(得分:0)
我看不到您在应用中使用ContactState
的位置。如果您不使用它并用它呈现ContactForm
组件,那么您将无法获得任何上下文值。您应该将其呈现为:
<ContactState>
<ContactForm />
</ContactState>
在您应用的合适位置。另外,您无法像这样获得contacts
和loading
:
const [ contacts, loading ] = state;
state
不是数组,在这里是对象。您应该使用:
const { contacts, loading } = state
您可以在下面找到代码的简化版本。我删除/更改了一些零件以便尽可能地运行它。您应该像@Asaf David在他们的答案中提到的那样来修复减速器,但这不是这里的主要问题。解决上下文问题后,您可以尝试修复减速器。
关于您的问题,如果您通过查看本示例来了解React的工作方式,那么您很容易感到困惑。因为Context
是一个高级概念(至少对于初学者而言)。另外,代码将useReducer
与Context
一起使用,这使事情变得更加复杂。如果您想了解React本身,请从beginner guide开始。
通过使用Context,我们可以将数据自上而下传递给最深层的组件。但是,为了使用该数据,应将那些组件呈现为上下文提供者的子级。
在您的代码中,您正在ContactState
中进行此操作,但从未使用过。另外,在该组件中,您正在使用useReducer
定义状态,并通过value
将该状态提供给上下文。
最后,在ContactForm
组件中,您正在使用useContext挂钩获取上下文数据。在您当前的代码中,因为您没有在提供程序中呈现此组件,所以contactContext
是未定义的,并且会出现错误。您无法从未定义中获得addContact
。
在我的示例中,我正在检索contacts
以显示某些内容。同样,我已经从您的代码中更改/删除了某些部分。
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" />