Reputation: 332
I'm using redux-saga in my React application. However, I'm a bit confused of how call and put effects should be implemented in the right way.
I want to add new data to my redux state, but whenever I use the set up listed bellow, new transactions object appears in my UI, but not in the JSON Server. If I refresh the page, the newly added item disappears.
One of the reasons my set up looks exactly like this is that I read that actions should be implemented with put effect whenever dispatching an action and that it's good for testing your Redux Saga.
What is the right way to use put effect in my situation?
I have the following set up:
Redux Saga
import { put, call, take, takeLatest} from "redux-saga/effects";
import {
ADD_TRANSACTION,
addTransaction
} from "state/transactions";
// POST
async function addTransactionData(payload) {
try {
const response = await fetch(`http://localhost:3010/transactions`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
});
const result = await response.json(payload);
return result;
} catch (error) {
throw error;
}
}
function* onAddTransactionData({ payload }) {
try {
const data = yield call(addTransactionData, payload);
yield put(addTransaction(data));
} catch (error) {
console.log(error);
}
}
export function* addTransactionWatcherSaga() {
yield take(ADD_TRANSACTION, onAddTransactionData);
}
Root Saga
import { all, call } from "redux-saga/effects";
import {
transactionsWatcherSaga,
removeTransactionWatcherSaga,
addTransactionWatcherSaga
} from "state/transactions";
export function* rootSaga() {
yield all([
call(transactionsWatcherSaga),
call(removeTransactionWatcherSaga),
call(addTransactionWatcherSaga)
]);
}
Action creators
// ....
export const addTransaction = ({
name,
amount,
description,
isExpense
} = {}) => {
return {
type: ADD_TRANSACTION,
payload: {
id: uuidv4(),
name,
amount,
description,
isExpense
}
};
};
//...
Reducer
/* ==============================================
============== TRANSACTIONS REDUCER ============
=============================================== */
import {
ADD_TRANSACTION,
REMOVE_TRANSACTION,
EDIT_TRANSACTION,
GET_TRANSACTIONS,
SET_TRANSACTIONS
} from "state/transactions";
const initialState = {
transactions: [],
transactionsLoader: false,
error: {
message: ""
}
};
export const transactionsReducer = (state = initialState, action) => {
switch (action.type) {
// ...
case ADD_TRANSACTION:
return {
...state,
transactions: [...state.transactions, action.payload]
};
// ....
default:
return state;
}
};
The only way I can succesfully dispatch a POST request is this, but that does not include put effect.
// POST
async function addTransactionData(payload) {
try {
const response = await fetch(`http://localhost:3010/transactions`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
});
const result = await response.json(payload);
console.log(result);
return result;
} catch (error) {
throw error;
}
}
function* onAddTransactionData({ payload }) {
console.log(payload);
try {
yield call(addTransactionData, payload);
} catch (error) {
console.log(error);
}
}
export function* addTransactionWatcherSaga() {
yield takeLatest(ADD_TRANSACTION, onAddTransactionData);
}
Upvotes: 1
Views: 3528
Reputation: 6633
Your saga is not configured correctly. Every time a ADD_TRANSACTION
action happens you are starting a saga for onAddTransactionData
but onAddTransactionData
ultimately puts the ADD_TRANSACTION
action, so you will loop (unless you get an error from the API).
Typically you'd have an action like ADD_TRANSACTION
which would start a saga onAddTransactionData
. That saga would make the API call and get back the result. Then the saga would put an action like LOAD_TRANSACTION_RESULTS
that would have the result of the API call in the payload, and your reducer would update your state with that result.
Upvotes: 2