无法读取未定义的属性“地图”-React Custom Hooks / useContext一种有效/一种无效

问题描述

我目前正在关注youtube上的教程,该教程使用来自webDevSimplified的react和socket.io构建whatsapp克隆:https://www.youtube.com/watch?v=tBr-PybP_9c

,这里是主要的回购协议:'https://github.com/WebDevSimplified/Whatsapp-Clone/tree/master/client

由于某种原因,我的自定义useConversation钩返回了未定义的状态,而我的useContacts钩却正常工作了。

这里是设置:

App.js:

import Dashboard from "./Dashboard";
import { ContactsProvider } from "../contexts/ContactsProvider";
import { Conversationsprovider } from "../contexts/Conversationsprovider";
import Test from "../components/test";///test

console.log(Test)//test purpose

function App() {
  const [id,setId] = useLocalStorage("id");

  const dashboard = (
    <ContactsProvider>
      <Conversationsprovider id={id}>
        <Dashboard id={id} />
      </Conversationsprovider>
    </ContactsProvider>
  );

  return id ? dashboard : <Login onIdSubmit={setId} />;
}



export default App;

ContactsProvider.js

import React,{ useContext } from "react";
import useLocalStorage from "../hooks/useLocalStorage";

const ContactsContext = React.createContext();

export function useContacts() {
  return useContext(ContactsContext);
}

export function ContactsProvider({ children }) {
  const [contacts,setContacts] = useLocalStorage("contacts",[]);//initalValue an empty array

  function createContact(id,name) {
    setContacts((prevContacts) => {
      return [...prevContacts,{ id,name }];
    });
  }
  return (
    <ContactsContext.Provider value={{ contacts,createContact }}>
      {children}
    </ContactsContext.Provider>
  );
}

Contacts.js-我的useContacts在这里工作

import React from "react";
import { ListGroup } from "react-bootstrap";
import { useContacts } from "../contexts/ContactsProvider";

export default function Contacts() {
  const { contacts } = useContacts();
  console.log(`contacts: ${contacts}`); //returns object,object as expected

  return (
    <ListGroup variant="flush">
      {contacts.map((contact) => (//visibly working in UI when commenting out the ListGroup in conversations.js
        <ListGroup.Item key={contact.id}>{contact.name}</ListGroup.Item>
      ))}
    </ListGroup>
  );
}

这是有问题的部分:

Conversationsprovider.js

import React,{ useContext } from "react";
import useLocalStorage from "../hooks/useLocalStorage";
import { useContacts } from "./ContactsProvider";

const ConversationsContext = React.createContext();

export function useConversations() {
  return useContext(ConversationsContext);
}

export function Conversationsprovider({ children }) {
  const [conversations,setConversations] = useLocalStorage(
    "conversations",[]);//as well empty array

  const { contacts } = useContacts();

  function createConversation(recipients) {
    setConversations((prevConversations) => {
      return [...prevConversations,{ recipients,messages: [] }];
    });
  }

  const formattedConversations = conversations.map((conversation) => {
    const recipients = conversation.recipients.map((recipient) => {
      const contact = contacts.find((contact) => {
        return contact.id === recipient;
      });
      const name = (contact && contact.name) || recipient;
      return { id: recipient,name };
    });

    return { ...conversation,recipients };
  });

  const value = {
    conversations: formattedConversations,createConversation,};

  return (
    <ConversationsContext.Provider value={{ value }}>
      {children}
    </ConversationsContext.Provider>
  );
  
}

以及导致错误的组件:

Conversations.js:

import React from "react";
import { ListGroup } from "react-bootstrap";
import { useConversations } from "../contexts/Conversationsprovider";

export default function Conversations() {
  const { conversations } = useConversations();
  console.log( `conversations: ${conversations}`)//returns undefined
  

  return (
    <ListGroup variant="flush">
      {conversations.map((conversation,index) => (//can't map because conversations is undefined
        <ListGroup.Item key={index}>
          {conversation.recipients
            .map(r => r.name)
            .join(",")}
        </ListGroup.Item>
      ))}
    </ListGroup>
  );
}

为清楚起见,这里是localStorage设置:

import { useEffect,useState } from 'react'

const PREFIX = 'whatsapp-clone-'

export default function useLocalStorage(key,initialValue) {
  const prefixedKey = PREFIX + key;
  const [value,setValue] = useState(() => {
    const jsonValue = localStorage.getItem(prefixedKey);
    if (jsonValue != null) return JSON.parse(jsonValue);
    if (typeof initialValue === "function") {
      return initialValue();
    } else {
      return initialValue;
    }
  });

  useEffect(() => {
    localStorage.setItem(prefixedKey,JSON.stringify(value));
  },[prefixedKey,value]);

  return [value,setValue];
}

我已经尝试解决这个问题了好几个小时了,而且想法不多了。我设置了一个test.js并将钩子导入到Test.js函数中。两者都分别返回其对象。

我已经使用npx create-react-app创建了应用程序,并通过yarn运行了它。

解决方法

问题出在您的ConversationsProvider.js内:

const value = {
  conversations: formattedConversations,createConversation,};

return (
  <ConversationsContext.Provider value={{ value }}>
    {children}
  </ConversationsContext.Provider>
);

以下几行都相同:

<ConversationsContext.Provider value={{ value }}>
<ConversationsContext.Provider value={{ value: value }}>
<ConversationsContext.Provider value={{ value: { conversations: formattedConversations,createConversation: createConversation } }}>

执行时:

const { conversations } = useConversations();

conversations将设置为undefined,因为返回的对象仅具有属性value

要解决此问题,请通过更改以下行来删除嵌套:

<ConversationsContext.Provider value={{ value }}>
// to
<ConversationsContext.Provider value={value}>

PS。这里是提高您的调试技能的提示。您已经做到了:

const { conversations } = useConversations();
console.log(conversations);

哪个登录了undefined。下一步将是不立即破坏对象,而是记录整个上下文值。

const contextValue = useConversations();
console.log(contextValue);

这将表明useConversations返回的对象不是您期望的对象。