I have a List
component to control same logic for list.
And I'm passing rendered react element as a props to the List
component.
But, useEffect in child component runs every time when the state changed.
Simplified code is like below:
saga
export function* fetchPosts(action) {
try {
const res = yield call_api(action.url);
yield put(actions.appendPosts(res.data.posts); // state.posts = state.posts.concat(action.payload.posts)
} finally {
yield put(actions.setLoading(false)); // state.meta.loading = false;
yield put(actions.setFetched(true)); // state.meta.fetched = true;
}
}
Posts.jsx
export const Posts = (props) => {
const { meta, posts } = useSelector((state) => state.postState);
const ListItems = () => {
return (
posts.map(d => (
<Post
...
/>
))
)
}
return (
<List
itemsElement={ <ListItems />} // $$typeof: Symbol(react.element)
...
/>
)
}
List.jsx
export const List = (props) => {
return (
<>
...
{ props.itemsElement }
...
</>
);
}
export default List
Post.jsx
export const Post = (props) => {
useEffect(() => {
// This code runs every time when the state changed
}, [])
return (
...
);
}
export default Post
And if I change Posts code like below, useEffect runs only once.
Posts.jsx
export const Posts = (props) => {
return (
posts.map(d => (
<Post
...
/>
))
)
}
How can I avoid this?
I'm thinking of changing the state at once like below,
setData: (state, action) => {
state.posts = state.posts.concat(action.payload.posts)
state.meta.loading = action.payload.loading
state.meta.fetched = action.payload.fetched
},
But I think maybe there is a better way. I'd appreciate it if you could point me to a better way.
I solved this issue.
This seems better for me in this case because I don't have to use useCallback
.
I didn't realize these two were different, but they are different.
Posts.jsx
export const Posts = (props) => {
const { meta, posts } = useSelector((state) => state.postState);
return (
<List
itemsElement={ // $$typeof: Symbol(react.element), type: Symbol(react.fragment)
<>
{
posts.map(d => (
<Post
...
/>
))
}
</>
}
...
/>
)
}
I need to wrap ListItems
function in useCallback
.
Posts.jsx
export const Posts = (props) => {
const { meta, posts } = useSelector((state) => state.postState);
const ListItems = useCallback(() => {
return (
posts.map(d => (
<Post
...
/>
))
)
}, [posts]);
return (
<List
itemsElement={ <ListItems />} // $$typeof: Symbol(react.element), type: () => {…}
...
/>
)
}