I have a React context that is working, but I want to bulk load items into the state.
The context has a reducer to add a single object to the array of objects that the Context stores. In the code (attached) the problem area is at the bottom in the Provider initialisation. In a useEffect, this does an api fetch call. The data comes back and is currently loaded one element at a time using the reducer. This is inefficient as it results in multiple render calls to the components listening to the context.
I am relatively new to React and Typescript and having difficulties getting the right coding and syntax to stop Typescript complaining.
I want to change the reducer to accept a 'load' action with a payload of an array of items that replace all current elements in the states array. I have a special load action and have beenn trying to get the payload for that action to be an array without changing the other actions which accept a single item. Either that or provide a new function that loads an array of objects into the state.
I have tried defining the payload in IAction to be payload: NewsItemType|Array, item NewsItemType }
Whatever I try, I get syntax errors in reducer: React.Reducer - mainly about the return type.
Can anyone help with advice on the best way to do this?
import * as React from "react";
/** Custom types */
import { ActionType } from "../custom-types";
import { NewsItemType } from "../custom-types";
import { apiRequest } from "../utils/Helpers";
const MAX_ITEMS = 30;
interface IState {
newsList: Array<NewsItemType>;
}
interface IAction {
type: ActionType;
payload: NewsItemType;
}
interface InewsContextInterface {
state: {
newsList: Array<NewsItemType>;
};
updateNewsList: React.Dispatch<IAction>;
}
const initialState: IState = { newsList: [] };
const reducer: React.Reducer<IState, IAction> = (state, action) => {
switch (action.type) {
case ActionType.add:
return {
newsList: [
action.payload,
...state.newsList.filter(
newsitem => newsitem._id !== action.payload._id
)
]
};
case ActionType.load:
return {
newsList: [...state.newsList, action.payload]
};
case ActionType.update:
return {
newsList: state.newsList.map(item => {
return item._id === action.payload._id ? action.payload : item;
})
};
case ActionType.delete:
return {
newsList: state.newsList.filter(
newsitem => newsitem._id !== action.payload._id
)
};
default:
throw new Error();
}
};
export const newsContext = React.createContext<InewsContextInterface>({
state: {
newsList: []
},
updateNewsList: () => { }
});
const { Provider } = newsContext;
const NewsProvider: React.FC<{ children: React.ReactNode }> = ({
children
}) => {
const [newsList, updateNewsList] = React.useReducer(reducer, initialState);
React.useEffect(() => {
apiRequest("http://localhost:5000/news/?limit=" + MAX_ITEMS, "get", true)
.then(news => {
if (news) {
Array.from(news).forEach((article: any) =>
updateNewsList({ type: ActionType.load, payload: article })
);
}
})
.catch(alert => {
console.error(alert);
});
}, []);
return (
<Provider value={{ state: newsList, updateNewsList }}>{children}</Provider>
);
};
export default NewsProvider;