反应上下文状态共享在同级组件中不起作用

时间:2020-09-06 03:18:03

标签: javascript reactjs react-component

我有一个简单的ToDo应用,其中包含一些组件。我要做的就是共享状态并更新上下文数据。我可以用props来做,但是将来,如果我有更多的组件,那么用props传递数据将太麻烦了。这是我的代码...

MainContext.js

import React, { createContext, useState } from "react";

const MainContext = createContext([{}, () => {}]);

const MainContextProvider = (props) => {
  const [state, setState] = useState({ title: "", body: "" });
  return (
    <MainContext.Provider value={[state, setState]}>
      {props.children}
    </MainContext.Provider>
  );
};

export { MainContext, MainContextProvider };

App.js

import MainContent from "./MainContent";
import { MainContextProvider } from "./MainContext";
export default class App extends Component {
  constructor() {
    super();
    this.state = {
    };
  }
  render() {
    return (
      <div className="container">
        <MainContextProvider>
          <Nav navigationItems={this.state.navItems} />
          <MainContent />
          <Footer />
        </MainContextProvider>
      </div>
    );
  }
}

MainContent.js

import React, { Component } from "react";
import CardType from "./CardType";
import FormData from "./FormData";

class MainContent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isLoading: true,
      initialCounter: 0,
      endCounter: 10,
      showLoadMore: true,
      cards: [],
    };
  }

  DeleteCard = (cardId) => {
    console.log(cardId);
    this.setState((prevState) => {
      const newCardArray = prevState.cards.filter((card) => card.id !== cardId);
      return {
        cards: newCardArray,
      };
    });
  };

  AddCard = (formData) => {
    this.setState((prevState) => {
      const newCardData = {
        id: prevState.cards[prevState.cards.length - 1].id + 1,
        userId: 12,
        title: formData.title,
        body: formData.description,
      };

      return {
        cards: [...prevState.cards, newCardData],
        // update the number of posts to +1
        endCounter: prevState.endCounter + 1,
      };
    });
  };
  componentDidMount() {
    fetch("https://jsonplaceholder.typicode.com/posts")
      .then((response) => response.json())
      .then((json) => {
        this.setState({ isLoading: false, cards: json });
      });
  }

  render() {
    if (this.state.isLoading) {
      return <h1>Loading....</h1>;
    } else {
      const cardsShow = this.state.cards.map((card) => {
        return (
          <CardType
            key={card.id}
            cardData={card}
            deleteCard={this.DeleteCard}
          />
        );
      });

      return (
        <main className="jumbotron">
          <h1>ToDo</h1>
          <FormData data={this.AddCard} />
          {cardsShow
            .reverse()
            .slice(this.state.initialCounter, this.state.endCounter)}
          {this.state.showLoadMore ? (
            <button
              onClick={() => {
                this.setState((prevState) => {
                  const newEndCounter = prevState.endCounter + 10;
                  const showLoadButton =
                    prevState.cards.length > newEndCounter ? true : false;

                  return {
                    endCounter: newEndCounter,
                    showLoadMore: showLoadButton,
                    // initialCounter: prevState.initialCounter + 10,
                  };
                });
              }}
            >
              Load More
            </button>
          ) : null}
        </main>
      );
    }
  }
}

export default MainContent;

CardType.js

import React, { useContext, useEffect } from "react";
import { MainContextProvider } from "./MainContext";

const CardType = (props) => {
  const [formData, setFormData] = useContext(MainContext);
  const updateValues = (card) => {
    setFormData(() => {
     return {
        title: card.title,
        body: card.body,
      };
    });
  };
  return (
    <div className="card">
      <div className="card-body">
        <h1>{props.cardData.title}</h1>
        <p className="card-text">{props.cardData.body}</p>
        <button onClick={() => props.deleteCard(props.cardData.id)}>
          Delete
        </button>
        <button onClick={() => updateValues(props.cardData)}>Update</button>
      </div>
    </div>
  );
};

export default CardType;

FormData.js

import React, { useState, useContext } from "react";
import { MainContextProvider } from "./MainContext";

const FormData = (props) => {
  const [formData, setFormData] = useContext(MainContext);
  const [title, setTitle] = useState(formData.title);
  const [description, setDescription] = useState("formData.body);

  const handleSubmit = (e) => {
    e.preventDefault();
    props.data({ title: title, description: description });
    setTitle("");
    setDescription("");
  };

  return (
    <form>
      <div className="form-group">
        <label>Name</label>
        <input
          className="col-sm-12"
          type="text"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
        />
      </div>
      <div className="form-group">
        <label>Description</label>
        <textarea
          className="col-sm-12"
          name="cardDescription"
          value={description}
          onChange={(e) => {
            setDescription(e.target.value);
          }}
        />
      </div>

      <p>{description}</p>
      <button onClick={(e) => handleSubmit(e)}>Add</button>
    </form>
  );
};

export default FormData;

我想要的是,当我单击每个待办事项的“更新”按钮时,我的表单应填充选定的待办事项数据并更新上下文,并在保存时将上下文更新回空字符串。我是React的新手。如果没有其他使用上下文的替代方法,请分享。

1 个答案:

答案 0 :(得分:2)

来自useContext文档:

不要忘记useContext的参数必须是上下文 对象本身:

  • 正确:useContext(MyContext)
  • 不正确:useContext(MyContext.Consumer)
  • 不正确:useContext(MyContext.Provider)

因此,在您的代码示例中,替换此行

const [formData, setFormData] = useContext(MainContextProvider);

使用

const [formData, setFormData] = useContext(MainContext);

为防止将来出现类似错误,您可以制作包装器挂钩以访问上下文。

MainContext.js

const useMainContext = () => useContext(MainContext)

现在您可以在任何此类功能组件中使用它们

const [formData, setFormData] = useMainContext();

实时演示

Edit React - UseContext