I make a post request in my application, using redux saga middleware.
Now, the code related to my post request looks like this:
function* postNewMessage(newMessage) {
console.log(newMessage)
const {var1, var2} = newMessage;
try {
const data = yield call(() => {
return fetch(myapi, {
method: 'POST',
headers: {
Accept: `application/json`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
first: var1,
second: var2
}),
})
});
console.log(data);
} catch (error) {
console.log(error);
}
}
function* addNewMessageSaga(action) {
console.log(action)
try {
yield postNewMessage(action.newMessage)
}catch (e) {
console.log(e)
}
}
function* watchNewMessage() {
takeEvery(POST_MESSAGE, addNewMessageSaga)
}
//bellow is my action which i call in my component
export const postMessage = (newMessage) => {
console.log(newMessage)
return {
type: POST_MESSAGE,
newMessage
};
};
This is all my code related with post request. Now i call the action inside component like:
postMessageOnApi({
var1: selector.data,
var2:selector.data1[0]
})};
When i click on the button to post my data, i get the values only inside postMessageOnApi, there console.log() shows my values, but in function * postNewMessage.. i don't get data.
Who knows where i made a mistake and how to solve the issue?
It looks like you forgot to use yield before takeEvery
function* watchNewMessage() {
yield takeEvery(POST_MESSAGE, addNewMessageSaga)
}
Related
I am working on a project which began last year, and the developers are not with me. They wrote this code :
import { put, takeLatest, all, call } from 'redux-saga/effects';
import { getUserByUsernameService } from '../../services/userServices';
import 'regenerator-runtime/runtime';
function* fetchUser() {
const response = yield call(getUserByUsernameService);
yield put({ type: 'FETCHED_USER', payload: response.data.user });
}
function* actionWatcher() {
yield takeLatest('FETCHING_USER', fetchUser);
}
export default function* rootSaga() {
yield all([actionWatcher()]);
}
Code of getUserByUsernameService :
import {
makeGetRequest,
makePostRequest,
makePutRequest,
makeDeleteRequest,
} from '../utils/reqUtils';
export const getUserByUsernameService = (params) => {
let headers = {
"Access-Control-Allow-Origin": "*"
};
makeGetRequest('/user', params, headers);
}
Code of makeGetRequest :
import axios from 'axios';
export const makeGetRequest = (endpoint, params = {}, headers) => {
const options = {
method: 'GET',
headers: { ...headers },
params: params,
url: endpoint,
};
return axiosInstance(options)
.then((resp) => resp.data)
.catch((e) => {
console.log(e);
throw e;
});
};
At runtime I get Cannot read property 'data' of undefined corresponding to the code
yield put({ type: 'FETCHED_USER', payload: response.data.user });
So what is wrong ?
The generator yield returns an object that you can iterate using next method.
I think you should use response.next().valute.data.user.
I think also as you consume the generator in fetchUser you should not yield the result of the call API method.
function* fetchUser() {
const response = call(getUserByUsernameService);
yield put({ type: 'FETCHED_USER', payload: response.next().value.data.user });
}
This is a simple typo — you forgot to return something from your getUserByUsernameService function! You are calling makeGetRequest but not returning the response. You want
return makeGetRequest('/user', params, headers);
I'm implementing get all image by type with redux-saga. I have 2 types, let's say, type kristik and type motif.
When I'm implementing type kristik, it got successful response, but when it comes to type motif, the response is error.
here my code that has the error in console
componentWillMount() => {
const { dispatch } = this.props;
dispatch(getAllMotif());
}
I got error in dispatch(getAllMotif()); in commponentWillMount()
Here my getAllMotif() code
getAllMotif(token) {
const path = `motif`;
const method = 'get';
return request.process(method, path, null, token);
},
Here my sagas getAllMotif code
export function* getAllMotif() {
try {
let { detail } = yield select(state => state.user);
const result = yield call(API.getAllMotif, detail.api_token);
yield put({
type: types.GET_ALL_MOTIF_SUCCESS,
payload: result,
});
} catch (err) {
yield put(handleError(err));
yield put({
type: types.GET_ALL_MOTIF_FAILURE,
payload: err,
});
}
}
here my reducer
case types.GET_ALL_MOTIF_SUCCESS:
return {
...state,
motif: [
...action.payload.data.data
]
};
here my request code
internals.process = (method, path, payload, token, contentType=internals.contentType) => {
const request = {
url: `${API_URL}/${path}`,
method: method,
headers: {
'Content-Type': contentType,
'Accept': 'application/json',
},
};
if (token) {
request.params = {
token: token,
};
}
if (payload) {
request.data = payload;
}
return axios.request(request)
.then(res => {
if (![200, 201].includes(res.status)) {
throw new Error(res.status);
}
return res.data;
})
.catch((error) => {
console.error(method, path, error);
return Promise.reject({
message: error.response.data.error,
code: error.response.status
});
});
};
I don't know why in this type get error, because in type kristik also have very similar code.
You didn't dispatch an action that wasn't a plain object, your function getAllMotif not return a plain object. That lead to the error here.
You should dispatch an normal action
getAllMotifAction(token) {
const path = `motif`;
const method = 'get';
return { type: 'GET_ALL_MOTIF', data: { path, method } };
},
Then in in saga, you catch this action and handle it with your saga function
takeLatest('GET_ALL_MOTIF', getAllMotif);
Good day everyone.
I have problem with this piece of code:
It's 2 function:
1.renderModal() - it's responsible for rendering ModalSuccess at the moment where data sucesfully will be added to databbase (to inform user about correctly fill form.
Component ModalSuccess when call it's render modal.
2.submitToServer - it's sending all data from redux-form to API.
In end of try, i trying call function renderModal.
How can i make it correctly?
function renderModal() {
return (
<div>
<ModalSuccess/>
</div>
);
}
//async function send to server
export async function submitToServer(values) {
//FUND
try {
let response = await fetch('endpoint', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...authHeader()
},
body: JSON.stringify(values),
});
let responseJson = await response.json();
return responseJson;
renderModal();
} catch (error) {
console.error(error);
}
I call submitTo server in 2 places:
1.
export var submit =(values) =>{
let isError =false;
if (isError) {
// throw new SumissionError(error);
} else{
return submitToServer(values)
.then(data =>{
if (data.errors) {
console.log(data.errors);
throw new SubmissionError(data.errors);
} else{
console.log(values)
console.log('server added data to database');
}
});
}
}
2.
<form onSubmit={handleSubmit(submitToServer)}>
I think you can restructure your code a bit better. Instead of returning the modal you can just mount the modal once and control its visibility leveraging the state.
Take a look at how I think your component should be structured.
class Comp extends React.Component {
state = {
isOpen: false
};
submitToServer = async values => {
try {
let response = await fetch("endpoint", {
method: "POST",
headers: {
"Content-Type": "application/json",
...authHeader()
},
body: JSON.stringify(values)
});
let responseJson = await response.json();
this.setState({ isOpen: true });
return responseJson;
} catch (error) {
console.error(error);
}
};
render() {
/* your component */
<ModalSuccess isOpen />;
}
}
As it stands your renderModal() invocation will never register since you are returning once the response it has been returned.
What you'd need to is something like this:
let responseJson = await response.json();
if (responseJson) {
renderModal();
}
export function* onFetchTree() {
yield takeLatest('FETCH_TREE', function* () {
try {
const response = yield call(fetch, '/myApi/user', {
method: 'GET',
headers: {
accept: 'application/json'
}
})
const responseBody = response.json();
yield put({ type: 'SET_TREE', payload: responseBody });
} catch (e) {
// yield put(fetchFailed(e));
return;
}
});
}
Learning to work with sagas, stuck on getting the actual data into my redux store. The above code which sends responseBody to the payload gives me a Promise object (because .json() returns that) which is great, except that I can't access the resolved Promise. I ended up on What does [[PromiseValue]] mean in javascript console and how to do I get it but this doesn't seem to work for me. I've tried adding .then() in a few ways, no luck. It seems to prevent the generator function from running at all.
If I just use response I get a Response object, which doesn't have the payload. What am I missing here? How do I get the right payload?
You need to wait for the server to send back the response.
export async function* onFetchTree() {
yield takeLatest('FETCH_TREE', function* () {
try {
const response = yield call(fetch, '/myApi/user', {
method: 'GET',
headers: {
accept: 'application/json'
}
})
const responseBody = await response.json()
yield put({ type: 'SET_TREE', payload: responseBody )}
};
} catch (e) {
// yield put(fetchFailed(e));
return;
}
});
}
I followed a pattern I found on this page that ended up working for me. I don't fully understand why the fetchTree helper is needed, but it doesn't work without it.
https://www.sigient.com/blog/managing-side-effects-with-redux-saga-a-primer-1
function fetchJson(url) {
return fetch(url, {
method: 'GET',
headers: {
accept: 'application/json'
}
})
.then(response => {
if (!response.ok) {
const error = new Error(response.statusText);
error.response = response;
throw error;
}
return response.json();
});
}
function fetchTree() {
return fetchJson('/myApi/user');
}
export function* onFetchTree() {
try {
const tree = yield call(fetchTree);
yield put({ type: 'SET_TREE', payload: tree });
} catch (e) {
yield put({
type: 'ERROR',
payload: e,
error: true,
});
}
}
I'm a newbie to redux-saga. Currently I'm working on a simple login example.
Take a look at these function:
function sendLoginRequest(data) {
const headers = { 'Accept': 'application/json' };
const url = LOGIN_URL;
const serialize = new FormData(data.event.target);
const loginData = {
username: serialize.get('email'),
password: serialize.get('password'),
client_secret: APP_SECRET_KEY,
client_id: APP_SECRET_ID,
grant_type: PASSWORD_GRANT_TYPE,
scope: '*',
}
return axios({
method: 'POST',
url: url,
data: loginData,
headers: headers,
});
}
export function* loginRequest(data) {
yield takeLatest(LOGIN_REQUEST, data => {
const response = sendLoginRequest(data);
console.log(response);
response
.then(function* (data) {
console.log(data);
yield put(LOGIN_SUCCESS, data.data);
})
.catch(function* (err) {
console.log(err);
yield put(LOGIN_FAILURE, err.response);
});
});
}
It's work perfect if I run middleware like this:
const sagaMiddleware = createSagaMiddleware();
const store = createStore(
rootReducer,
applyMiddleware(sagaMiddleware)
);
sagaMiddleware.run(loginRequest);
But then I add a new rootSaga:
export default function* rootSaga() {
yield all([
fork(loginRequest),
fork(loginSuccess),
fork(loginFailure)
]);
}
And I run the rootSaga instead of loginRequest saga:
const sagaMiddleware = createSagaMiddleware();
const store = createStore(
rootReducer,
applyMiddleware(sagaMiddleware)
);
sagaMiddleware.run(rootSaga);
And now these new code doesn't work at all.
when I try to console.log(response); in loginRequest generator function, it's shown that the promise has been resolved. And it doesn't run the then-catch.
Can anyone help me clear this out?
Thank you~
Please try this:
export function* loginRequest(data) {
yield takeLatest(LOGIN_REQUEST, data => {
const response = yield call(sendLoginRequest, data);
if (response.status >= 200 && response.status < 300) {
yield put(LOGIN_SUCCESS, data.data);
return;
}
yield put(LOGIN_FAILURE, err.response);
});
}
The thing to note is sendLoginRequest returns a promise. redux-saga is designed to work with promises without using .then(). You can yield call() any function that returns a promise and redux-saga will wait for the promise to resolve before executing the next line of code.