My component structure is like this
App
| - TopHeader
| - Main
|- Thread
| - Modal
Topheader contains a button for opening a Modal which has a textbox. I'm passing textbox data from Modal->App and changing newPost
value so that Main could re-render upon detecting change. But this is causing somehow infinite re-rendering. Here is my code for this.
App
:
import "./scss/App.scss";
import Sidebar from "./components/Sidebar";
import TopHeader from "./components/TopHeader";
import Main from "./components/Main";
import RightPan from "./components/RightPan";
import Modal from "./components/Modal";
import React, { useEffect, useState } from "react";
function App() {
const [shouldModalPopUp, setShouldModalPopUp] = useState(false);
const [newPost, setNewPost] = useState();
function handleNewThreadButtonClick() {
setShouldModalPopUp(true);
}
function handleCloseButtonClick() {
setShouldModalPopUp(false);
}
function onPostSubmitHandler(newPost) {
console.log("before preventDefault");
setNewPost(newPost);
handleCloseButtonClick();
}
return (
<div className="App">
{/* <Sidebar key={1} /> */}
<TopHeader
newThreadButtonClickHandler={handleNewThreadButtonClick}
key={2}
/>
<Main newPost={newPost} key={3} />
{/* <RightPan key={4} /> */}
<Modal
shouldModalPopUp={shouldModalPopUp}
handleCloseButtonClick={handleCloseButtonClick}
onPostSubmitHandler={onPostSubmitHandler}
key={5}
/>
</div>
);
}
export default App;
Main
import "../scss/App.scss";
import Thread from "./Thread";
import React, { useEffect, useState } from "react";
function Main(props) {
const [allThreads, setAllThreads] = useState([]);
function onPostSubmitHandler(newPost) {
setAllThreads((prevState) => {
const newList = [];
for (let i = 0; i < prevState.length; i ) {
newList.push(prevState[i]);
}
newList.push({ post: newPost, id: Math.random() });
console.log("previous state length: " prevState.length);
console.log("new state length: " newList.length);
return newList;
});
}
console.log("props received in Main" JSON.stringify(props));
function onDeleteHandler(id) {
console.log("inside ondeletehandler " id);
const newList = [];
for (let i = 0; i < allThreads.length; i ) {
if (allThreads[i].id != id) {
newList.push(allThreads[i]);
}
}
setAllThreads(newList);
}
if (props.newPost) {
onPostSubmitHandler(props.newPost);
}
return (
<div className="main">
<span>main</span>
{allThreads.map((thread) => {
console.log("appending posts in list");
console.log(thread.post);
return (
<Thread
post={thread.post}
id={thread.id}
onDeleteThread={onDeleteHandler}
/>
);
})}
</div>
);
}
export default Main;
Modal
import React, { useState } from "react";
function Modal(props) {
const [post, setPost] = useState("");
function submitHandler(event) {
event.preventDefault();
props.onPostSubmitHandler(post);
}
function onChangeHandler(event) {
event.preventDefault();
setPost(event.target.value);
}
if (props.shouldModalPopUp) {
return (
<div>
<div className="backdrop" />
<div className="modal">
<form onSubmit={submitHandler}>
<textarea
id="txtArea"
className="postTextArea"
placeholder="please type your post.."
rows="20"
cols="80"
onChange={onChangeHandler}
/>
<br />
<br />
<button
type="button"
className="button"
onClick={props.handleCloseButtonClick}
>
close
</button>
<button type="submit" className="button">
Submit
</button>
</form>
</div>
</div>
);
}
return null;
}
export default Modal;
TopHeader
import "../scss/App.scss";
function TopHeader(props) {
return (
<div className="topHeader">
<button
className="button button--newthread"
onClick={props.newThreadButtonClickHandler}
>
Start a new Thread
</button>
</div>
);
}
export default TopHeader;
Thread
import { useState } from "react/cjs/react.development";
import "../scss/App.scss";
function Thread(props) {
const [post, setPost] = useState(props.post);
const [id, setId] = useState(props.id);
console.log("reached thread method");
const [isDeleted, setIsDeleted] = useState(false);
function deleteHandler() {
setIsDeleted(true);
props.onDeleteThread(id);
}
return (
<div className="thread">
{post}
<button className="button" onClick={deleteHandler}>
delete
</button>
</div>
);
}
export default Thread;
CodePudding user response:
In the Main
:
if (props.newPost) {
onPostSubmitHandler(props.newPost);
}
after you newPost
is set and passed as a prop to this component, this if
block will always return true
.
Due to which, the method onPostSubmitHandler
will be executed whenever the Main
component re-renders (i.e., when the props/state of the component changes, in this case newPost
).
Moreover, you're scheduling a state change in the first line of this method onPostSubmitHandler
which is causing you infinite re-rendering.
You should use useEffect
hook instead of the if
check, with your props.newPost
variable in the list of dependencies.
You can use object destructuring, to get the newPost
variable and then pass it in the dependency array like this:
const { newPost } = props;
useEffect(() => {
onPostSubmitHandler(newPost);
}, [newPost]);