我在控制台中收到此错误:无法读取未定义的属性“ some”

时间:2019-09-04 08:47:22

标签: javascript reactjs react-hooks react-context

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

2 个答案:

答案 0 :(得分:0)

在减速器中,添加联系人时,您散布了错误的状态键。这应该解决它:

case 'ADD_CONTACT':
  return {
    contacts: [...state.contacts, action.payload]
  }

答案 1 :(得分:0)

我看不到您在应用中使用ContactState的位置。如果您不使用它并用它呈现ContactForm组件,那么您将无法获得任何上下文值。您应该将其呈现为:

<ContactState>
  <ContactForm />
</ContactState>

在您应用的合适位置。另外,您无法像这样获得contactsloading

const [ contacts, loading ] = state;

state不是数组,在这里是对象。您应该使用:

const { contacts, loading } = state

您可以在下面找到代码的简化版本。我删除/更改了一些零件以便尽可能地运行它。您应该像@Asaf David在他们的答案中提到的那样来修复减速器,但这不是这里的主要问题。解决上下文问题后,您可以尝试修复减速器。

关于您的问题,如果您通过查看本示例来了解React的工作方式,那么您很容易感到困惑。因为Context是一个高级概念(至少对于初学者而言)。另外,代码将useReducerContext一起使用,这使事情变得更加复杂。如果您想了解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" />