无法读取未定义的 属性 'map' - React Custom Hooks / useContext 一个有效/一个无效

Cannot read property 'map' of undefined - React Custom Hooks / useContext one works / one doesn't

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

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

由于某种原因我的自定义 useConversation 钩子 return 未定义而我的 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 函数中导入了钩子。 return 分别是它们的对象。

我使用 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 返回的对象不是您期望的那样。