I'm new with typescript, so I was creating a blog-app in which user can write blog(only text) with title and blog body. When, Add button is clicked blog should be added to blogs array and user should be redirected to Blogs page.
Problem: When Add button is clicked blog is added to array but user is not redirected.
Below is code:
Add.tsx
interface Props {
setBlogs: (blog: IBlog) => void;
}
function Add(props: Props) {
const [info, setInfo] = useState<IBlog>({ title: "", body: "" });
const [clicked, setClicked] = useState<boolean>(false);
function handleInfoChange(
e:
| React.ChangeEvent<HTMLTextAreaElement>
| React.ChangeEvent<HTMLInputElement>
) {
const name: string = e.target.name;
const value: string = e.target.value;
setInfo(
(preInfo): IBlog => {
return { ...preInfo, [name]: value };
}
);
}
function addClick() {
if (info.body.length > 0 && info.title.length > 0) {
props.setBlogs(info);
setClicked(true);
}
}
React.useEffect(() => {
console.log("Clicked", clicked);
}, [clicked]);
return (
<div className="add">
<div className="compileArea">
<input
type="text"
id="title"
name="title"
autoComplete="off"
placeholder="Title"
value={info.title}
onChange={handleInfoChange}
/>
<textarea
name="body"
id="body"
cols={30}
rows={10}
placeholder="Type here"
value={info.body}
onChange={handleInfoChange}
></textarea>
<div className="controls">
<button id="add-button" onClick={addClick}>
Add
</button>
</div>
</div>
{clicked ? <Redirect to="./blogs" /> : null}
</div>
);
}
export default Add;
IBlog looks like this:
type IBlog = {
title: string;
body: string;
};
App.tsx
function App() {
const [blogs, setBlogs] = React.useState<IBlogs>([]);
function setblogs(blog: IBlog): void {
setBlogs([...blogs, blog]);
}
React.useEffect(() => {
console.log("Blog", blogs);
}, [blogs]);
return (
<div className="App">
<Header />
<Switch>
<Route path="/" exact />
<Route
path="/add"
exact
component={() => <Add setBlogs={setblogs} />}
/>
<Route path="/blogs" exact component={() => <Blogs blogs={blogs} />} />
</Switch>
</div>
);
}
export default App;
The thing is if I remove setBlogs([...blogs, blog]);
in function setblogs
in file App.tsx
. User is redirected to Blogs page successfully.
If someone want to reproduce the error full project is here.
the issue happens at Route declaration at app:
<Route
path="/add"
exact
component={() => <Add setBlogs={setblogs} />}
/>
when you declare a function while passing to a prop, a new function will be created everytime App
component updates. In this way, when you call props.setBlogs(info)
, App
updates and component
receives a new function causing Add
to rerender everytime.
to avoid that, one way you could pass a function like component={renderAdd}
. createAdd is the same function, but alredy declared and wrapped in useCallback like:
const renderAdd = useCallback(
() => <Add setBlogs={setblogs} />,
[setBlogs],
)
other way, which I think is cleaner, is to pass your component as child to Route:
<Route
path="/add"
exact
>
<Add setBlogs={setblogs} />
</Route>