i have a simple ToDo app which has some components. All i want to do is to share state and update context data. I can do it with props but in future, if i have more components then it will be too cumbersome to pass data with props. Here is my code...
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;
I want is when i click on Update button of each todo, my form should populate with selected TODO data and update the context and on save, update my context back to empty string. I am newbie in React. Please share if there is any other alternate way to do this without using context.
From the useContext
docs:
Don't forget that the argument to useContext must be the context object itself:
- Correct:
useContext(MyContext)
- Incorrect:
useContext(MyContext.Consumer)
- Incorrect:
useContext(MyContext.Provider)
So in your code sample, replace this line
const [formData, setFormData] = useContext(MainContextProvider);
with
const [formData, setFormData] = useContext(MainContext);
To prevent similar errors in the future, you can make a wrapper hook to access the context.
In MainContext.js
const useMainContext = () => useContext(MainContext)
Now you can use them in any functional components like this
const [formData, setFormData] = useMainContext();
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.