Displaying HTTP Request Reponse With Redux Saga - javascript

Heyy so I want to get a json from a request with react-saga! I was wondering how I would get the data that the my saga yields, I have an idea to call a generator function in the componentWillMount that watches for the 'REQUEST_DONE' action with takeLatest and then rerenders.
But I think it would a bad idea to use react-saga in one of my components. Guidance please
My saga file:
export function* Saga() {
yield fetch(url, {
method: 'GET',
headers: {
'Accept': '...',
'Content-Type': 'application/json'
}
})
.then(response => {
return response.json();
})
.then(json => {
return json;
})
.catch(ex => {
console.log('parsing failed', ex)
})
}
export default function* watchAsync() {
console.log(yield Saga().next().value); // gets the value correctly
yield* takeLatest('BLAH', Saga);
}
My component
...
componentWillMount() {
const { store } = this.context;
store.dispatch({type: 'BLAH'});
// I want the request data
}
render() { ... }

EDIT webpackbin DEMO
call fetch and yield result
import { take, put, call } from 'redux-saga/effects';
function fetchData() {
return fetch(url)
.then(res => res.json() )
.then(data => ({ data }) )
.catch(ex => {
console.log('parsing failed', ex);
return ({ ex });
});
}
function* yourSaga(action) {
const { data, ex } = yield call(fetchData);
if (data)
yield put({ type: 'REQUEST_DONE', data });
else
yield put({ type: 'REQUEST_FAILED', ex });
}
export default function* watchAsync() {
yield* takeLatest('BLAH', yourSaga);
}
then connect component and slice needed data
class App extends Component {
...
componentWillMount() {
this.props.dispatch({type: 'BLAH'});
}
render(){
return (<div>Data: {this.props.data}</div>);
}
}
export default connect( state =>({
data:state.data
}))(App);

Related

How to retrieve the value of a yield function call?

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);

next.js is not returning items on getStaticProps()

I am trying to load items to my next.js page and it will fail:
import {getadminInfo} from '../../dataFetch/adminInfo'
import {addItem} from '../../dataFetch/catalog'
import {useState} from "react"
import { getList } from '../../dataFetch/catalogList'
export async function getStaticProps() {
const adminData = await getadminInfo()
const catlist = await getList()
return {
props: {
catlist,
adminData
}
}
}
export default function Main({allPostsData, adminData, catlist}) {
}
My function is :
export function getList() {
const pageInfo = {
page_size : "10",
page:"1"
}
const url = "http://localhost:8000/api/catalog/list?page_size="+pageInfo.page_size+"&page="+pageInfo.page;
try {
fetch(url, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
fData=JSON.parse(JSON.stringify(data.response))
console.log("Returned catalog")
return fData
})
.catch(error => console.log(error))
} catch (err) {
console.log(err)
}
}
The API works and I get the right info back but I cannot load it to the page:
Error: Error serializing .catlist returned from getStaticProps in "/admin/main".
Reason: undefined cannot be serialized as JSON. Please use null or omit this value.
I found the issue. I did not implement the fetch correctly. It should have been async.
The reason I did not get the info is because nothing was returned.

I don't know how to get data from fetch correctly

I have a Users class where I want to get data from the server for later writing it to state and passing data from state to the child component
export default class Users extends Component {
constructor(props) {
super(props);
this.state = {
users: this.getUsers(),
};
}
getUsers = async () => {
await return fetch(`http://localhost:3001/users`, {
method: 'POST',
accept: 'application/json'
}).then(res => {
if(res.ok) {
res.json();
}
})
}
}
this is what the console shows me when I output data about this.state.users
I tried to look for similar situations, but I didn't find anything worthwhile, so I ask for help here. I would be grateful for any advice or help. I'm only learning asynchrony in js
if you use async await, you don't have to pass callback function, just await the promises and update the state incase of successful response.
getUsers = async () => {
try {
const response = await fetch(`http://localhost:3001/users`, {
method: 'POST',
accept: 'application/json'
});
const users = await response.json();
this.setState({ users });
} catch (error) {
console.log(error);
}
}
and instead of calling getUsers function from the constructor, use componentDidMount
componentDidMount() {
this.getUsers();
}
and your state should be initially null or an empty array
this.state = {
users: []
};
Add componentDidMount and call getUsers and set state.
this.state = {
users: [],
};
getUsers = async () => {
return await fetch(`http://localhost:3001/users`, {
method: 'POST',
accept: 'application/json'
}).then(response => response.json())
.then(res => { this.seState({ users: res })})
.catch(e => { console.log(e)})
}
componentDidMount = () => {
this.getUsers()
.catch(e => console.log(e)
}

Problem with nested fetch request in React

New to React, I'm currently trying to create a data table with data from an API.
I want to have a first fetch, and then run another with response from the first (id) in order to complete my table.
Here is my code :
class HomePage extends React.Component {
constructor(props) {
super(props);
this.state = {
user: {},
data: []
};
}
componentDidMount() {
this.setState({
user: JSON.parse(localStorage.getItem('user'))
}, function () {
this.loadAllObjectsInfo()
});
}
// Fetch all object info in order to fill the table
loadAllObjectsInfo() {
const requestOptions = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'bbuser': this.state.user.userId,
'bbtoken': this.state.user.secret
},
};
fetch('https://xxxxx/api/objects', requestOptions)
.then(response => response.json())
.then((data) => {
this.setState({ data: data })
})
}
With this code, I have the data I want to render my table but I need to run another fetch to get other info with the id coming from the first request.
How can I do that nested fetch request ?
Thanks a lot,
Matthieu
You can easily manage this with async/await:
async loadAllObjectsInfo() {
const requestOptions = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'bbuser': this.state.user.user
'bbtoken': this.state.user.secret
},
};
let response = await fetch('https://xxxxx/api/objects', requestOptions);
let data = await response.json();
// here is another fetch - change to fit your request parameters (this is just example)
let info = await fetch('https://xxxxx/api/objects/' + data.id);
this.setState({ data });
}
You can read more about async function.
#JourdanM, you should return a new fetch request from one of the then handlers. I've made a simple snippet for you. There are no data validators and spinners. This is a simple showcase. =)
A fetch request returns a promise, and you can chain promises by simply returning them from the then handlers. Here is a good article about it, it has great examples: https://javascript.info/promise-chaining
function fetchUser (user) {
return fetch(`https://api.github.com/users/${user.login}`)
}
class User extends React.Component {
state = {
user: null
}
componentDidMount () {
fetch("https://api.github.com/users")
.then(response => response.json())
.then(users => fetchUser(users[0]))
.then(response => response.json())
.then(user => {
this.setState({user})
})
}
render () {
return (
<div>
<pre>{JSON.stringify(this.state.user, null, 2)}</pre>
</div>
)
}
}
ReactDOM.render(<User />, document.querySelector("#root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
You can write the code as below.
fetch('https://xxxxx/api/objects', requestOptions)
.then(response => response.json())
.then((res1) => {
fetch('https://xxxxx/api/objects', requestOptions)
.then(response => response.json())
.then((res2) => {
this.setState({ data: res2 });
});
});
Hope this will work for you!
You can also use axios like below
axios.post(url, data, header).then(res => {
if(res.status === 200){
console.log('1st data')
axios.post(url, data, header)
.then(response => {
if (response.status === 200) {
console.log('2nd data')
} else {
console.log('2nd error')
}
});
}else{
console.log('1st error')
}
});

'redux-saga' not working asynchronously

I'm trying (learning) to make an asynchronous post request using redux-saga, but i'm getting a synchronous behavior.
this is the code i'm using:
import {AUTH_REQUEST} from '../constants/authentication';
import {authSuccess, authError} from '../actions/login';
import {takeEvery, call, put, fork, all} from 'redux-saga/effects';
import axios from 'axios';
const authenticate = (username, password) => {
axios
.post('http://localhost:8000/api/auth/login/', {username, password})
.then((response) => {
console.log('RESPONSE: ', response.data);
return response.data;
})
.catch((error) => {
throw error;
});
};
function* watchAuthRequest({username, password, resolve, reject}) {
try {
const result = yield call(authenticate, username, password);
console.log('RESULT', result);
yield put(authSuccess(result));
yield call(resolve);
} catch (error) {
yield put(authError(error));
yield call(reject, {serverError: 'Something bad happend !'});
}
}
const authSaga = function* authSaga() {
yield takeEvery(AUTH_REQUEST, watchAuthRequest);
};
export default function* rootSaga() {
yield all([
fork(authSaga),
]);
};
and when i submit the form (i'm using redux-form), this is why i get in my console logs:
RESULT: undefined
RESPONSE: Object {user: Object, token: "04a06266803c826ac3af3ffb65e0762ce909b07b2373c83b5a25f24611675e00"}
and even the authSuccess action is getting dispatched with an empty payload (result)
am i doing something wrong here ?
You're missing a return:
const authenticate = (username, password) => {
return axios
.post('http://localhost:8000/api/auth/login/', {username, password})
.then((response) => {
console.log('RESPONSE: ', response.data);
return response.data;
})
.catch((error) => {
throw error;
});
};

Categories

Resources