Issue
I am trying to call API with Redux, and it calls again based on a form submission. which means if query is none, it returns all lists or it returns lists that match the query.
// List.tsx
import React, { useEffect, useState } from "react";
import { connect } from "react-redux";
import { getFeatures, increasePage } from "../../redux/featuresSlice";
import { FeatureItem } from "../../components/featureItem";
interface IFeatureProp {
featureObject: any;
getFeaturesWith: any;
}
const FeatureList = ({
featureObject,
getFeaturesWith,
}: // page,
IFeatureProp) => {
const [keywords, setKeywords] = useState("");
const fetchData = async () => {
getFeaturesWith(featureObject.page, keywords);
};
const handleSubmit = (e: any) => {
e.preventDefault();
fetchData();
};
useEffect(() => {
console.log(keywords);
fetchData();
}, []);
return (
<div className="max-w-screen-xl mx-auto mt-8">
<div className="px-12">
<div className="relative">
<div className="relative">
<form onSubmit={handleSubmit}>
<div className="absolute top-0 bottom-0 left-0 flex items-center px-5">
</div>
<input
type="text"
placeholder="Search..."
value={keywords}
onChange={e => setKeywords(e.target.value)}
/>
</form>
</div>
<ul>
{featureObject.features
.map((c: any) => {
return (
<FeatureItem
id={c.id}
name={c.name}
desc={c.desc}
/>
);
})}
</ul>
</div>
</div>
</div>
);
};
function mapStateToProps(state: any) {
return { featureObject: state.featuresReducer.explore };
}
function mapDispatchToProps(dispatch: any) {
return {
getFeaturesWith: (page: any, keyword: string) =>
dispatch(getFeatures(page, keyword)),
increasePageWith: () => dispatch(increasePage(1)),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(FeatureList);
//featuresSlice.js
import { createSlice } from "@reduxjs/toolkit";
import api from "../api";
const featuresSlice = createSlice({
name: "features",
initialState: {
explore: {
page: 1,
features: [],
},
favs: [],
},
reducers: {
setExploreFeatures(state, action) {
const { explore } = state;
const { payload } = action;
payload.features.forEach(payloadFeature => {
const exists = explore.features.find(
savedFeature => savedFeature.id === payloadFeature.id
);
if (!exists) {
explore.features.push(payloadFeature);
}
});
state.explore.page = payload.page;
},
},
});
export const { setExploreFeatures, increasePage, setFavs, setFav } =
featuresSlice.actions;
export const getFeatures = (page, keyword) => async (dispatch, getState) => {
const {
usersReducer: { token },
} = getState();
try {
const {
data: { results },
} = await api.features(token, page, keyword);
dispatch(
setExploreFeatures({
features: results,
page: 1,
})
);
} catch (e) {
console.warn(e);
}
};
export default featuresSlice.reducer;
when I submit keyword, it works as I expected at backend.
[07/Feb/2022 01:55:23] "GET /api/v1/features/?search=abcd HTTP/1.1" 200 3615
And I see only lists that match the query in redux-debugger. But it doesn't re-render the page, which means I only see whole lists.
Is there what I can do for updating state?
Solution
The issue is that your reducer is mutating the state, which should never be the case within the Redux ideology.
Instead, you should be creating a new state object (with your changes applied) and return it from the reducer. And if you don't want to modify the state - just return the original one kept intact. All your reducers should be like that.
reducers: {
setExploreFeatures(state, action) {
const { payload } = action;
const newFeatures = payload.features.filter(x => !state.explore.features.some(y => y.id === x.id));
return { ...state, explore: { ...state.explore, features: [...state.explore.features, ...newFeatures], page: payload.page } };
},
},
along these lines ^^
Answered By - Kostiantyn Ko
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.