How to use React Redux Hooks to load spinners - reactjs

I am trying to load spinner using react-redux hooks (useSelector and useDispatch). I am able to fetch data but not loader (in my case showLoader and hideLoader)
Expectation: when I click the refresh button I want to load spinner (in background it will refresh the data). Before clicking the button I am able to fetch data using useEffect hook.
//ActionCreators.js
export const EVENT_LOG = "EVENT_LOG";
export const EVENT_FAILURE = "EVENT_FAILURE";
export const SHOW_LOADER = "SHOW_LOADER";
export const HIDE_LOADER = "HIDE_LOADER";
//Actions.js
import {
EVENT_LOG,
EVENT_FAILURE,
SHOW_LOADER,
HIDE_LOADER,
} from "./actionCreators";
import { readList } from "./APIUtilsNew";
export const readLogs = (path) => {
return (dispatch) => {
readList(path)
.then((data) =>
dispatch(
{
type: EVENT_LOG,
payload: data,
},
console.log("EventLog Actions: ", data)
)
)
.catch((error) => {
dispatch({
type: EVENT_FAILURE,
payload: error,
});
throw error;
});
};
};
export const showLoader = () => (dispatch) => {
dispatch({
type: SHOW_LOADER,
});
};
export const hideLoader = () => (dispatch) => {
dispatch({
type: HIDE_LOADER,
});
};
//Reducers.js
import {
EVENT_LOG,
EVENT_FAILURE,
HIDE_LOADER,
SHOW_LOADER,
} from "../../actionCreators/index";
export const initialState = {
loading: false,
eventData: [],
eventError: false,
};
const eventReducer = (state = initialState, action) => {
switch (action.type) {
case EVENT_LOG:
return {
...state,
eventData: action.payload,
};
case EVENT_FAILURE:
return {
...state,
eventError: action.payload,
};
case HIDE_LOADER:
return {
...state,
loading: false,
};
case SHOW_LOADER:
return {
...state,
loading: true,
};
default:
return state;
}
};
export default eventReducer;
//React Component
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { readLogs, showLoader, hideLoader } from "./eventActions";
import { FormattedMessage } from "react-intl";
import { XGrid } from "#material-ui/x-grid";
import { CSVLink } from "react-csv";
import IconBtn from "./IconBtn";
import MaterialTheme from "./MaterialTheme";
import { ThemeProvider as MuiThemeProvider } from "#material-ui/core/styles";
import Refresh from "./Refresh";
export default function EventsLog() {
const dispatch = useDispatch();
const eventLogs = useSelector(
(state) => state.eventReducer.eventData.data || []
);
const show = useSelector((state) => state.eventReducer.loading);
const hide = useSelector((state) => state.eventReducer.loading);
useEffect(() => {
dispatch(readLogs("/events"));
}, [dispatch]);
const update = () => {
dispatch(showLoader());
dispatch(hideLoader());
};
let rows = eventLogs.map((obj, index) => {
return (rows = {
id: index + 1,
Time: obj.time,
dateTime: obj.dateTime,
ID: obj.deviceId
});
});
const columns = [
{
field: "Time",
flex: 1,
type: "dateTime",
renderHeader: () => <FormattedMessage id={"time"} />
},
{
field: "dateTime",
flex: 1,
type: "dateTime",
renderHeader: () => <FormattedMessage id={"dateTime"} />
},
{
field: "ID",
flex: 1,
renderHeader: () => <FormattedMessage id={"id"} />
}
];
return (
<div>
<h1>
<FormattedMessage id="event.eventLog" />
<span>
<IconBtn iconLabel="refresh" />
</span>
<CSVLink data={rows} filename={"Log.csv"}>
<IconBtn iconLabel="cloud_download" onClick={update} />
</CSVLink>
</h1>
<div style={{ height: "90%", width: "100%" }}>
<MuiThemeProvider theme={MaterialTheme}>
<Refresh />
<XGrid
pageSize={50}
rowsPerPageOptions={[25, 50, 100]}
rows={rows}
columns={columns}
pagination={true}
hideFooterSelectedRowCount={true}
/>
</MuiThemeProvider>
</div>
</div>
);
}
This is the component where my spinner resides. I want to fetch this component while loading spinner
//Refresh Component
import React from "react";
export default function Refresh() {
return <div>Spinner....</div>;
}
I saw few examples online, where I found everything is in class components
// component Example
class FullPageLoader extends Component {
state = { }
render() {
const {loading} = this.props;
if(!loading) return null;
return (
<div class="loader-container">
<div className="loader">
<img src={LoaderGif} />
</div>
</div>
);
}
}
const mapStateToProps = state => ({ loading: state.application.loading })
export default connect(mapStateToProps)(FullPageLoader);
// Another Component
updateProfile = () =>{
this.props.dispatch( showLoader() )
Axios.post(`https://jsonplaceholder.typicode.com/users`, { user : { name : 'Test User' } })
.then(res => {
console.log( res );
this.props.dispatch( hideLoader() )
})
/* setTimeout(() => {
this.props.dispatch( hideLoader() )
}, 2000); */
}
<Button bsStyle="info" pullRight fill onClick={this.updateProfile} >
Update Profile
</Button>
Can somebody help me how to convert the above class to functional based component and instead of using mapStateToProps to hooks (or) please tell me how to load the spinner using react-redux hooks. I appreciate the help!

More easier way is to show and hide the loader in the action itself. Before the promise, setLoader as true. And in then and catch you can hide loader.
export const readLogs = (path) => {
return (dispatch) => {
showLoader();
readList(path)
.then((data) => {
hideLoader();
dispatch(
{
type: EVENT_LOG,
payload: data,
},
console.log("EventLog Actions: ", data)
)
})
.catch((error) => {
hideLoader();
dispatch({
type: EVENT_FAILURE,
payload: error,
});
throw error;
});
};
};
if it has to be done in the component itself, You can add a delay rather than calling them immediately. There doesn't seem to be any action that is happening here.
const update = () => {
dispatch(showLoader());
setTimeout(() => {
dispatch(hideLoader());
}, 1000);
};

Related

component unexpectedly re-mounts every time i dispatch new state. Im using ContextProvider with useReducer

I using Context.Provider + useReducer, i have function "fetchCars()" in my context for fetching cars which depends on selected filter value
May be noob question, but i can't understand why consumer-component named "Filters.jsx" is mounting every time after i changed filter value. And because of this i cant save values in useState of Filter.jsx component
Codesandbox link
https://codesandbox.io/s/peaceful-morse-21zj6m?file=/src/components/Filters.jsx
in Codesandbox you can see console print when filter changed
CarsContextProvider.jsx
import { useReducer, createContext, useCallback } from "react";
export const CarsContext = createContext()
const getCarsFromServer = (status) => {
// dummy fetch
const dataFromServer = [
{ id: 1, name: 'Volvo', status: 'notAvailable' },
{ id: 2, name: 'BMW', status: 'inStock' },
{ id: 3, name: 'Mercedes', status: 'notAvailable' },
{ id: 4, name: 'Audi', status: 'notAvailable' },
{ id: 5, name: 'Opel', status: 'inStock' },
{ id: 6, name: 'Renault', status: 'inStock' },
]
return new Promise((resolve) => {
setTimeout(() => {
if (status === 'all') {
return resolve(dataFromServer)
}
resolve(dataFromServer.filter(item => item.status === status))
}, 500);
})
}
const reducer = (state, action) => {
switch (action.type) {
case 'pending':
return { ...state, loading: true }
case 'success':
return { ...state, loading: false, items: action.payload }
case 'error':
return { ...state, loading: false, error: action.payload }
case 'setFilter':
return { ...state, statusFilter: action.payload }
default:
break;
}
}
const initState = {
items: [],
loading: false,
error: '',
statusFilter: 'all',
}
const CarsContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initState)
const fetchCars = useCallback(async () => {
try {
dispatch({ type: 'pending' })
const data = await getCarsFromServer(state.statusFilter)
dispatch({ type: 'success', payload: data })
} catch (error) {
dispatch({ type: 'error', payload: error })
}
}, [state.statusFilter])
return (
<CarsContext.Provider value={{ state, dispatch, fetchCars }}>
{children}
</CarsContext.Provider>
)
}
export default CarsContextProvider
App.jsx
import CarsScreen from "./components/CarsScreen";
import CarsContextProvider from "./context/CarsContext";
function App() {
return (
<CarsContextProvider>
<CarsScreen />
</CarsContextProvider>
);
}
export default App;
CarsScreen.jsx
import CarsList from "./CarsList"
const CarsScreen = () => {
return (
<div>
<CarsList />
</div>
)
}
export default CarsScreen
CarsList.jsx
import { useContext, useEffect } from "react"
import { CarsContext } from "../context/CarsContext"
import Filters from "./Filters"
const CarsList = () => {
const { state, fetchCars } = useContext(CarsContext)
useEffect(() => {
fetchCars()
}, [fetchCars])
if (state.loading) return <h3>loading...</h3>
return (
<>
<Filters />
<hr />
<ul>
{state.items.map((car => <li key={car.id}>{car.name}</li>))}
</ul>
</>
)
}
export default CarsList
Filters.jsx
import { useState, useEffect, useContext } from "react"
import { CarsContext } from "../context/CarsContext"
const Filters = () => {
const [localState, setLocalState] = useState('init')
const { state, dispatch } = useContext(CarsContext)
useEffect(() => {
// There is my question! Why console.log executing every time i change filter select option ?
console.log('component mounted');
}, [])
const filterChangeHandler = (e) => {
//and also localState could not change, because this component every time mounts with init value
setLocalState('filter changed')
// this dispatch changes filter value, and items fetching from server
dispatch({ type: 'setFilter', payload: e.target.value })
}
return (
<div>
<select
name="stockFilter"
onChange={filterChangeHandler}
defaultValue={state.statusFilter}
>
<option value="all">show all</option>
<option value="inStock">in stock</option>
<option value="notAvailable">not available</option>
</select>
<p>Filters local state is : {localState}</p>
</div>
)
}
export default Filters
Codesandbox link
https://codesandbox.io/s/peaceful-morse-21zj6m?file=/src/components/Filters.jsx
Tried to comment React.StrictMode line in index.js., but no effect
Is it possible to avoid this unwanted mount Filters.jsx component?
Found a problem.
Problem was in component GoodsList.jsx loading status was dismounting Filter.jsx
if (state.loading) return <h3>loading...</h3>
fixed version of GoodsList.jsx
import { useContext, useEffect } from "react"
import { GoodsContext } from "../context/GoodsContextProvider"
import GoodsFilter from "./GoodsFilter"
const GoodsList = () => {
const { state, fetchGoods } = useContext(GoodsContext)
useEffect(() => {
fetchGoods()
}, [fetchGoods])
return (
<>
<GoodsFilter />
{state.loading ? <h3>loading...</h3>
:
<>
<hr />
<ul>
{state.items.map((good => <li key={good.id}>{good.name} - {good.quantity}</li>))}
</ul>
</>
}
</>
)
}
export default GoodsList

Error: Invalid hook call. Hooks can only be called inside of the body of a function component - When adding Loader component

I'm working a Nextjs - Typescript project. I'am trying to add a Loader component. while page is loading loader component is true. otherwise false.
This is my code:
LoderContext.ts
import React, { useReducer } from "react";
import { NextPage } from "next";
type State = { loading: boolean };
type Action = { type: "startLoading" } | { type: "endLoading" };
type Dispatch = (action: Action) => void;
export const LoadingContext =
React.createContext<{ state: State; dispatch: Dispatch } | undefined>(
undefined
);
const initialState = {
loading: true,
};
const { Provider } = LoadingContext;
const reducer = (state: State, action: Action) => {
switch (action.type) {
case "startLoading":
return {
loading: true,
};
case "endLoading":
return {
loading: false,
};
default:
throw state;
}
};
const LoadingProvider: NextPage = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
const value = { state, dispatch };
return <Provider value={value}>{children}</Provider>;
};
export function useLoading() {
const context = React.useContext(LoadingContext); // error is somewhere here
if (context === undefined) {
throw new Error("useCount must be used within a CountProvider");
}
return context;
}
export default LoadingProvider;
Loder.tsx
const Loader: NextPage = () => {
return (
<div className={styles.loader_wrapper}>
<div className={styles.content}>
<span className={styles.loading_text}>Loading...</span>
</div>
</div>
);
};
export default Loader;
Index.tsx
import Loader from "../components/Loader/Loader";
import { useLoading } from "../context/LoadingContext";
import { useRouter } from "next/dist/client/router";
const { state, dispatch } = useLoading();
const router = useRouter();
useEffect(() => {
router.events.on("routeChangeStart", () => {
dispatch({ type: "startLoading" });
});
return () => {
router.events.off("routeChangeStart", () => {
dispatch({ type: "endLoading" });
});
};
}, [dispatch, router]);
useEffect(() => {
dispatch({ type: "endLoading" });
}, [dispatch, router]);
const Home: NextPage = () => {
return (
<>
{state.loading ? (
<Loader />
) : (
<>
<div>
// other components
</div>
</>
)}
</>
);
};
I got this error. Error: Invalid hook call. Hooks can only be called inside of the body of a function component
What are the mistakes in my code?
Your index page is calling useLoading, useEffect and useRouter outside the Home component.
Refer react documentation. It clearly mentions the issue you're facing.
Hooks can only be called inside the body of a function component.
It should be something like this.
import Loader from "../components/Loader/Loader";
import { useLoading } from "../context/LoadingContext";
import { useRouter } from "next/dist/client/router";
const Home: NextPage = () => {
const { state, dispatch } = useLoading();
const router = useRouter();
useEffect(() => {
router.events.on("routeChangeStart", () => {
dispatch({ type: "startLoading" });
});
return () => {
router.events.off("routeChangeStart", () => {
dispatch({ type: "endLoading" });
});
};
}, [dispatch, router]);
useEffect(() => {
dispatch({ type: "endLoading" });
}, [dispatch, router]);
return (
<>
{state.loading ? (
<Loader />
) : (
<>
<div>
// other components
</div>
</>
)}
</>
);
};

React Redux not rendering after data change

I know this question has been asked multiple times but I cannot seem to find an answer. I have a component named DynamicTable which renders JSON as a data table. It has been tested in multiple other pages and works correctly. Here I have put it into a React-Bootstrap tab container. The data pull works correctly but the page is not re-rendering when the fetch is complete.
Here is the code I am using
//RetailRequests.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import FormComponent from '../Elements/FormComponent';
import TabContainer from 'react-bootstrap/TabContainer';
import Tabs from 'react-bootstrap/Tabs';
import Tab from 'react-bootstrap/Tab';
import DynamicTable from '../Elements/DynamicTable';
const mapStateToProps = (state) => {
return {
RequestData: state.RetailRequests,
siteMap: state.siteMap
}
}
const mapDispatchToProps = (dispatch) => {
return {
Retail_Request_Fetch: () => { return dispatch(Retail_Request_Fetch()) },
Retail_Request_Insert: (data) => { return dispatch(Retail_Request_Insert(data)) },
Retail_Request_Delete: (id) => { return dispatch(Retail_Request_Delete(id)) },
Retail_Request_DeleteAll: () => { return dispatch(Retail_Request_DeleteAll()) }
}
}
class RetailRequests extends Component {
constructor(props) {
super(props);
var roles = props.siteMap.siteMapData.userRoles.toLowerCase();
this.state = {
showAdmin: roles.indexOf('admin') >= 0 || roles.indexOf('systems') >= 0
}
}
componentDidMount() {
this.props.Retail_Request_Fetch();
}
// ...
render() {
let rows = this.buildData();
let data = this.props.RequestData?this.props.RequestData.adminData:null;
return (
<div style={{ transform: 'translateY(10px)' }} >
<TabContainer>
<div className='col-md-10 offset-1' >
<Tabs defaultActiveKey='general' id='retail_reports_tab_container' >
<Tab eventKey='general' title='Enter New Request'>
<h1> Retail Requests</h1>
<FormComponent rows={rows} submit={this.submitFn} />
</Tab>
<Tab eventKey='admin' title='Admin' disabled={!this.state.showAdmin}>
<h1>Manager Data</h1>
<DynamicTable
data={data}
border="solid 1px black"
title={"Retail Requests Admin"}
/>
</Tab>
</Tabs>
</div>
</TabContainer>
</div>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(RetailRequests);
//RetailRequestsCreator.js
export const Retail_Request_Fetch = () => (dispatch, getState) => {
var init = JSON.parse(JSON.stringify(fetchInit()));//copy to not modify the original
var myReq = new Request(`${process.env.REACT_APP_HOST}/Retail_Request`, init);
dispatch({
type: ActionTypes.REQUESTS_LOADING
})
return fetch(myReq)
.then((response) => {
if (response.ok) {
return response;
}
else {
var error = new Error("Error " + response.statusText);
error.response = response;
throw error;
}
}, (error) => {
var err = new Error(error.message);
throw err;
})
.then((response) => { return response.json() })
.then((RequestData) => {
if (RequestData !== "False") {
console.log(RequestData)
dispatch({
type: ActionTypes.REQUESTS_LOADED,
payload: RequestData
})
}
else CurrentPage_Update({ componentId: 'NotAllowed' });
})
.catch((err) => {
dispatch({
type: ActionTypes.REQUESTS_FAILED,
payload: "Error: " + err.message
})
});
}
//RetailRequestReducer.js
import * as ActionTypes from '../ActionTypes';
export const retailRequests = (state = {
isLoading: true,
errMess: null,
currentPage: []
}, action) => {
switch (action.type) {
case ActionTypes.REQUESTS_LOADED:
return { ...state, isLoading: false, errMess: null, adminData: action.payload };
case ActionTypes.REQUESTS_LOADING:
return { ...state, isLoading: true, errMess: null, adminData: {} };
case ActionTypes.REQUESTS_FAILED:
return { ...state, isLoading: false, errMess: action.payload, adminData: null };
default:
return state;
}
}
I am sure that there is something simple in this but the only error I am getting is that the data I am using, this.props.RequestData, is undefined although after the fetch I am getting proper state change in Redux.
It looks like you have problem in mapStateToProps
const mapStateToProps = (state) => {
return {
RequestData: state.retailRequests, // use lower case for retailRequests instead of RetailRequests
siteMap: state.siteMap
}
}

redux props receiving data twice

I am using redux to get the async data and response. In the below component when i post recipe and from server i get response through redux the success modal popup twice. reducer is running only once i have checked eveything, only component has problem. the problem could be with the lifecycle method.
import React, { Component } from 'react';
import 'antd/dist/antd.css';
import {withRouter} from 'react-router-dom';
import {connect} from 'react-redux';
import * as actionCreators from '../../actions/recipe-action/index';
import { Modal, Button } from "antd";
import Spinner from '../../UI/spinner';
class PostRecipe extends Component {
state = {
url: '',
visible: false,
}
showModal = () => {
this.setState({ visible: true });
};
onChangeHandler = (e) => {
this.setState({[e.target.name]: e.target.value});
}
handleOk = e => {
this.props.recipe(this.state.url);
this.setState({url: ""});
this.setState({ visible: false });
};
handleCancel = e => {
this.setState({ visible: false });
};
render() {
const { postRecipes } = this.props;
if(postRecipes.loading) {
return <Spinner />;
}else if(postRecipes.success.ok) {
// this success model popup twice after uploading the recipe
Modal.success({
content: "Recipe Uploaded"
});
}else if(postRecipes.failure.error) {
Modal.error({
title: "Error while uploading recipe",
});
}
return (
<div>
<div>
<Button type="primary" onClick={this.showModal}>
Add Recipe
</Button>
<Modal
title="Add Recipe"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
>
<input
style={{ width: "100%", padding: "5px", fontSize: "15px" }}
type="text"
placeholder="enter the url"
name="url"
value={this.state.url}
onChange={this.onChangeHandler}
/>
</Modal>
</div>
</div>
);
}
}
const mapStateToProps = ({ postRecipeReducers }) => {
const { recipe: { post: postRecipes } } = postRecipeReducers;
return {
postRecipes
}
};
const mapStateToDispatch = dispatch => {
return {
recipe: (url) => dispatch(actionCreators.postRecipes(url))
}
}
export default withRouter(connect(mapStateToProps, mapStateToDispatch)(PostRecipe));
// my action creators
import {POST_RECIPE_LOADING, POST_RECIPE_SUCCESS, POST_RECIPE_FAILURE, POST_RECIPE_RESET} from '../types';
import {GET_RECIPE_LOADING, GET_RECIPE_SUCCESS, GET_RECIPE_FAILURE, GET_RECIPE_RESET} from '../types';
import Parse from 'parse';
export const postRecipes = (url) => async(dispatch) => {
try {
dispatch({type: POST_RECIPE_LOADING, payload: null});
const {data} = await Parse.Cloud.run('post_recipe', {url: url});
dispatch({type: POST_RECIPE_SUCCESS, payload: data});
} catch(e) {
dispatch({type: POST_RECIPE_FAILURE, payload: {message: e.message}})
}
}
export const getRecipes = () => async (dispatch) => {
try {
dispatch({type: GET_RECIPE_LOADING, payload: null});
const {data} = await Parse.Cloud.run('get_recipe');
dispatch({type: GET_RECIPE_SUCCESS, payload: data});
} catch(e) {
dispatch({type: GET_RECIPE_FAILURE, payload: {message: e.message}})
}
};
Try this:
handleOk = e => {
this.props.recipe(this.state.url);
this.setState({url: "", visible: false});
};
state variable of class is a object with two keys: url and visible. You have to set both at once.
I would try implementing a constructor function to make sure that you have this bound to your local state.
In this code block,
handleOk = e => {
this.props.recipe(this.state.url);
this.setState({url: ""});
this.setState({ visible: false });
};
you could set the whole state in one line like this,
handleOk = e => {
this.props.recipe(this.state.url);
this.setState({url: "", visible: false});
}
I don't know that this will fix your problem. Just a bit of house keeping.

How can I avoid the infinite loop in useEffect?

I need your help. I'm creating an app with useContext and useReducer hooks and I a have problems. I have a function to get all notes from my database. I called that function inside off useEffect hook:
import React, { useContext, useEffect } from "react";
import { useTranslation } from "react-i18next";
//Context
import AuthContext from "../../context/auth/authContext";
import NoteContext from '../../context/notes/noteContext';
//Components
import { Row, Col, Container, Button } from "react-bootstrap";
import Canva from '../Canva/Canva';
import Note from '../Note/Note';
const Dashboard = () => {
const { t, i18n } = useTranslation();
const authContext = useContext(AuthContext);
const { authUser, user } = authContext;
const noteContext = useContext(NoteContext);
const { notes, getNotes, addNote } = noteContext;
useEffect(() => {
getNotes();
}, []);
return (
<>
<Container>
<Row>
<Col sm={12} md={10}>
<Button onClick={() => addNote()} type='button' className='mb-2'>
AƱadir elemento
</Button>
<Canva>
{notes && (notes.map(note => {
return (
<Note key={note._id} note={note} />
)
}))}
</Canva>
</Col>
</Row>
</Container>
</>
);
};
export default Dashboard;
If I called that function that way, my state doesn't change:
notes: undefined
But if I introduce a dependency inside of useEffect, my app goes into an infinite loop. For example:
useEffect(() => {
getNotes();
}, [notes])
//Or:
useEffect(() => {
getNotes()
}, [getNotes])
How can I avoid the infinite loop?
You need to use 2 useEffect hooks, one for fetch data and second to proceed it:
useEffect(() => {
getNotes();
}, []);
useEffect(() => {
if (notes && notes.length) {
....setState or what else
}
}, [notes]);
My note state looks like:
import React, { useReducer } from 'react';
import clientAxios from '../../config/clientAxios';
import NoteContext from './noteContext';
import NoteReducer from './noteReducer';
import {
GET_NOTES,
ADD_NOTE,
DELETE_NOTE,
UPDATE_NOTE,
} from '../../types';
const NoteState = ({ children }) => {
const initialState = {
notes: [],
noteError: false,
};
const [state, dispatch] = useReducer(NoteReducer, initialState);
const getNotes = async () => {
try {
const response = await clientAxios.get('/user/Notes');
dispatch({
type: GET_NOTES,
payload: response.data
})
} catch (error) {
console.log(error.response);
}
}
const addNote = async data => {
try {
const response = await clientAxios.post('/addNote', data);
dispatch({
type: ADD_NOTE,
payload: response.data.data
})
} catch (error) {
console.log(error.response);
}
}
const updateNote = async (id, { title, description }) => {
try {
const response = await clientAxios.put(`updateNote/${id}`, { title, description });
console.log(response.data);
dispatch({
type: UPDATE_NOTE,
payload: response.data
})
} catch (error) {
console.log(error.response)
}
}
const deleteNote = async id => {
try {
await clientAxios.put(`/deleteNote/${id}`);
dispatch({
type: DELETE_NOTE,
payload: id
})
} catch (error) {
console.log(error.response);
}
}
return(
<NoteContext.Provider
value={{
notes: state.notes,
noteError: state.noteError,
getNotes,
addNote,
updateNote,
deleteNote,
}}
>
{children}
</NoteContext.Provider>
);
}
export default NoteState;
and my reducer:
import {
GET_NOTES,
ADD_NOTE,
DELETE_NOTE,
UPDATE_NOTE,
} from '../../types';
export default (action, state) => {
switch(action.type) {
case GET_NOTES:
return {
...state,
notes: action.payload
}
case ADD_NOTE:
return {
...state,
notes: [...state.notes, action.payload]
}
case UPDATE_NOTE:
return {
...state,
notes: state.notes.map(note => note._id === action.payload._id ? action.payload : note)
}
case DELETE_NOTE:
return {
...state,
notes: state.notes.filter(note => note._id !== action.payload),
}
default:
return state;
}
}

Resources