#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();
Живая демонстрация
Комментарии:
1. Спасибо. Это остановило ошибку, но не заполнило поля формы. Я вижу, что состояние моего контекста меняется, но заголовок и описания остаются пустыми
FormData.js
. Есть идеи?2. используйте useEffect для обнаружения изменений и обновления ваших входных данных. Я добавил живой пример, чтобы вы могли поиграть с @Preet.