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]);
