This is my notesSlice:
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
const url = "http://localhost:8000/";
const initialState = {
notices: [],
};
export const getNotices = createAsyncThunk(
"notices/getNotices",
async (_name, thunkAPI) => {
try {
const resp = await axios(`${url}notices`);
return resp.data;
} catch (error) {
return thunkAPI.rejectWithValue("something went wrong");
}
}
);
const noticesSlice = createSlice({
name: "notices",
initialState,
reducers: {
filterNotices: (state, { payload }) => {
console.log(state.notices)
console.log(payload)
if (payload.searchOption === "Simple") {
state.notices = state.notices.filter((note) =>
note.title.toLowerCase().includes(payload.search.toLowerCase())
);
} else if (payload.searchOption === "Advanced") {
state.notices = state.notices.filter(
(note) =>
note.description
.toLowerCase()
.includes(payload.search.toLowerCase()) ||
note.tags.filter((tag) => {
tag.toLowerCase().includes(payload.search.toLowerCase());
})
);
}
},
},
extraReducers: (builder) => {
builder
.addCase(getNotices.fulfilled, (state, { payload }) => {
state.notices = payload;
})
In payload of filterNotice
method, I am sending object with search
input and searchOption
. How should I search through all notices every time I call filterNotice
method? And is this even the correct way to do it?
Don't mutate your source of truth by filtering it. The filtered result should be computed/derived "state" from the notices
state and the search
value.
Store the search type and value in state and use selector functions to compute the derived state.
const initialState = {
notices: [],
searchOption: "Simple",
searchValue: "",
};
const noticesSlice = createSlice({
name: "notices",
initialState,
reducers: {
setSearch: (state, action) => {
const { searchOption, search } = action.payload;
state.searchOption = searchOption;
state.searchValue = search.toLowerCase();
},
...
},
...
});
export const selectFilteredNotices = state => {
const { notices, searchOption, searchValue } = state.path.to.notices;
switch(searchOption) {
case "Simple":
return notices.filter((note) =>
note.title.toLowerCase().includes(searchValue)
);
case "Advanced":
return notices.filter((note) =>
note.description.toLowerCase().includes(searchValue) ||
note.tags.some((tag) => tag.toLowerCase().includes(searchValue))
);
default:
return notices;
}
};
The UI would dispatch the setSearch
action to update the search value and use the useSelector
hook and selectFilteredNotices
function to selected the filtered notices value.
const dispatch = useDispatch();
const filteredNotices = useSelector(selectFilteredNotices);
...
dispatch(setSearch({ searchOption, search }));
...
Create a new filteredNotices
array in state, and when dispatching the filterNotices
action you will filter the untouched state.notices
array and return/update the state.filteredNotices
array. This leaves the original notices data intact.
const initialState = {
notices: [],
filteredNotices: [],
};
const noticesSlice = createSlice({
name: "notices",
initialState,
reducers: {
filterNotices: (state, action) => {
const { searchOption, search } = action.payload;
const searchValue = search.toLowerCase();
if (searchOption === "Simple") {
state.filteredNotices = state.notices.filter((note) =>
note.title.toLowerCase().includes(searchValue)
);
} else if (searchOption === "Advanced") {
state.filteredNotices = state.notices.filter((note) =>
note.description.toLowerCase().includes(searchValue) ||
note.tags.some((tag) => tag.toLowerCase().includes(searchValue))
);
}
},
...
},
...
});