Совместное использование состояния контекста реакции не работает в родственных компонентах

#javascript #reactjs #react-component

#javascript #reactjs #реагирующий компонент

Вопрос:

у меня есть простое приложение 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;
  

Я хочу, чтобы, когда я нажимаю на кнопку обновления каждого todo, моя форма должна заполняться выбранными данными TODO и обновлять контекст, а при сохранении обновляйте мой контекст до пустой строки. Я новичок в React. Пожалуйста, поделитесь, есть ли какой-либо другой альтернативный способ сделать это без использования контекста.

Ответ №1:

Из 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();
  

Живая демонстрация

Редактировать React - useContext

Комментарии:

1. Спасибо. Это остановило ошибку, но не заполнило поля формы. Я вижу, что состояние моего контекста меняется, но заголовок и описания остаются пустыми FormData.js . Есть идеи?

2. используйте useEffect для обнаружения изменений и обновления ваших входных данных. Я добавил живой пример, чтобы вы могли поиграть с @Preet.