send get request with id in redux saga - reactjs

I have some list on the first page and I want to by click set Request to get single items by redux-saga but it's run before click and return undefined and not show the response of the first request of list. I want to pass some slug to the end of the request and return details of the item.
Cards.js
SetSlugItems(Items){
this.props.onFetchArticle(Items.slug)
}
render(){
console.log('this.props.articles.articles',this.props.articles.articles)
return (
<ContainerContent>
<ContentHolder>
<CardHolderStyled >
{this.props.articles.articles&&this.props.articles.articles.map((Items,index)=>{
return(
<Link to={{ pathname: '/singleArticle', state: { index:index} }} >
<CardItemStyled key={index} onClick={()=>this.SetSlugItems(Items)} >
</CardItemStyled>
</Link>
)})}
</CardHolderStyled>
<SideBar/>
</ContentHolder>
</ContainerContent>
);}}
Action.js
function fetchArticleAction (slug) {
return {
type: GET_SINGLE_ARTICLE,
slug
};
}
function getArticleSuccessSingle (receiveArticleSingle) {
return {
type: GET_SINGLE_ARTICLE_SUCCESS,
receiveArticleSingle
};
}
function getArticleFailureSingle () {
return {
type: GET_SINGLE_ARTICLE_FAILURE
};
}
Saga.js
function* getArticleSingle (action) {
try {
const receiveArticleSingle = yield call[fetchArticleSingle(action.slug)] ;
yield put(getArticleSuccessSingle(receiveArticleSingle));
} catch (err) {
yield put(getArticleFailureSingle());
}
}
const fetchArticleSingle = (slug) => {
return fetch('http://localhost:3000/api/articles/'+slug, {
method:'GET',
headers:{
'Content-Type': 'application/json',
'X-Requested-With':'XMLHttpRequest',
}
})
.then(response => response.json())
};
function* watchGetArticle () {
yield takeEvery(GET_ARTICLE, getArticle);
yield takeEvery(GET_SINGLE_ARTICLE, getArticleSingle);
}
and Reducer.js
import {
GET_SINGLE_ARTICLE_SUCCESS,
GET_SINGLE_ARTICLE_FAILURE
} from '../constants';
const initialStateSingle = [];
const SingleArticleReducer = (stateSlug = initialStateSingle, action={}) => {
switch (action.type) {
case GET_SINGLE_ARTICLE_SUCCESS:
return action.receiveArticleSingle;
case GET_SINGLE_ARTICLE_FAILURE:
return [];
default:
return stateSlug;
}
};
export default SingleArticleReducer

call[fetchArticleSingle(action.slug)]
This is accessing a property on the call function, and that property is undefined. Instead you'll want to call call, as in:
call(fetchArticleSingle, action.slug);

Related

how to pass token in headers in below reactjs codesandbox link

https://codesandbox.io/s/login-authentication-usecontext-66t9t?file=/src/index.js
Here how we can pass token in headers for any other pages in codesandbox link. In my code i have action file like this. im getting my response in localstorage.how can i pass my accesstoken here as headers in this page.
import axios from 'axios';
export const upiAction = {
upi,
};
function upi(user) {
return (dispatch) => {
var data = {
upiId: user.upiId,
accountNumber: user.accountNumber,
};
axios
.post('http://localhost:9091/upiidcreation', data
)
.then((res) => {
console.log("res", (res));
const { data } = res;
alert(JSON.stringify(data.responseDesc));
// window.location.pathname = "./homes";
if (data.responseCode === "00") {
window.location.pathname = "./home"
}
})
.catch(err => {
dispatch(setUserUpiError(err, true));
alert("Please Check With details");
});
};
}
export function setUserUpi(showError) {
return {
type: 'SET_UPI_SUCCESS',
showError: showError,
};
}
export function setUserUpiError(error, showError) {
return {
type: 'SET_UPI_ERROR',
error: error,
showError: showError,
};
}

JSON return [Object][Object]

I'm trying to get data from json file, but it always return [object][object]
Here is json file https://my-json-server.typicode.com/khanh21011999/demo/user
Here is request function to get data
export function requestGetUser() {
return axios({
method: 'get',
url: 'https://my-json-server.typicode.com/khanh21011999/demo/user',
});
}
Here is the method i use to get data
function* loginSaga(action) {
console.log('Saga is working')
const getJson = yield call(requestGetUser) //same
const getJsonData = JSON.stringify(getJson)
const getJsonUsername = String(getJsonData.username)
console.log('GetJson '+getJson)
const getJsonPassword = String(getJsonData.password)
if (String(action.data.username) === getJsonUsername) {
if (String(action.data.password) === getJsonPassword) {
console.log('saga login success')
yield put({type: 'LOGIN_SUCCESS'})
SaveToAsyncStorage(action.data)
}
else {
console.log('saga password fail')
}
}
else {
console.log("saga user fail")
}
}
export {loginSaga}
It return like this
Weird things is i use a online tutorial to get data, it work with that(data show in above image)
worked method
export function* handleGetUser(action) {
try {
const response = yield call(requestGetUser); //same
const { data } = response;
yield put(setUser(data));
} catch (error) {
console.log(error);
}
}
setUser
export const setUser = (user) => ({
type: actionList.SET_USER,
user,
});
GetUserInfo
export const GetUserInfo = (user, password) => {
return{
type: actionList.GET_USER_INFO,
data: {user, password}, //same??
}
};
Here is export function
export function* watchSaga() {
yield takeLatest(GET_USER, handleGetUser); //work
yield takeLatest(GET_USER_INFO,loginSaga) //notwork
}
One different is the worked method have reducer
const initState = {
user: undefined,
};
const User = (state = initState, action) => {
switch (action.type) {
case actionList.SET_USER:
const {user} = action;
return {...state,user};
default:
return state;
}
};
export default User;
But my method have none,(i thoght data was save in state action)
console.log('GetJson ' + getJson); You're printing a concatenation of strings and objects. Modify it to console.log('GetJson ', getJson);
Besides, you should return the res.data from axios.get() method, see Response Schema. You will get the plain object of JavaScript, there is no need to use JSON.stringify().
import axios from 'axios';
export function requestGetUser() {
return axios({
method: 'get',
url: 'https://my-json-server.typicode.com/khanh21011999/demo/user',
}).then((res) => res.data);
}

Using local storage with react-redux?

I have a react redux project that looks like this:
Reducer:
export function contentReducer(state = { loading: true }, action) {
switch (action.type) {
case types.RETRIEVE_CONTENT_SUCCESS:
return { ...state, loading: false, contentResults: action.payload.results }
default:
return state;
}
};
Action:
export function loginSuccess(loginResult) {
return { type: types.LOGIN_SUCCESS, loginResult };
}
export function login(formData) {
return function (dispatch) {
return submitLogin(formData).then(umvToken => {
dispatch(loginSuccess(umvToken));
}).catch(error => {
throw (error);
});
};
}
The Api itself:
export function submitLogin(login) {
var form = new FormData()
form.append('userIdentifier', login.email)
form.append('password', login.password)
return fetch("http://localhost:8080/login/login/umv",
{
method: "POST",
headers: {
'Accept': 'application/x-www-form-urlencoded;charset=UTF-8',
},
body: form
}).then(function (response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
})
.then(function (token) {
localStorage.setItem('umvToken', token.text())
return token
})
.catch(function (error) {
console.log('Looks like there was a problem: \n', error);
})
}
function mapStateToProps(state) {
return {
login: state.login.loginResponse
}
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(thunks, dispatch)
};
}
class ConnectedHome extends Component {
constructor() {
super();
this.processForm = this.processForm.bind(this);
}
processForm(form) {
this.props.actions.login(form)
}
render() {
return (
<div>
{this.ButtonAppBar()}
<LoginForm onSubmit={this.processForm} />
</div >
);
}
}
const Home = connect(mapStateToProps, mapDispatchToProps)(ConnectedHome);
export default Home
However when I try to access local storage from the home page like this:
var result = localStorage.getItem('umvToken')
console.log(result)
I get the following message:
[object Promise]
If I replace the setting of local storage with this:
localStorage.setItem('umvToken', 'test')
then the console.log will show test. But it doesn't work with the actual value. I'm not really sure how to resolve this?
Thanks for your help!
From first .then function in api call, return response.json(); or return response.text();. If you just return response, its a promise, which won't get resolved, unless you do response.json() or response.text(), depending on the type of response you are expecting.
I'm pretty sure token.text() returns a Promise, so you will need to wait for it to resolve to get the actual token string, i.e.
.then(function (token) {
return token.text();
})
.then(tokenText => {
localStorage.setItem('umvToken', tokenText);
})
Use the following code
.then(function (token) {
localStorage.setItem('umvToken', JSON.stringify(token))
return token
})

Managing dependent state

I have following state:
{
programmeCodes, // generated from asynch resource
programmeColors // depends on programmeCodes
}
and following actions for asynch resource:
export const REQUEST_PROGRAMME_CODES = 'REQUEST_PROGRAMME_CODES'
export const RECEIVE_PROGRAMME_CODES = 'RECEIVE_PROGRAMME_CODES'
function requestProgrammeCodes() {
return {
type: REQUEST_PROGRAMME_CODES,
}
}
export function fetchProgrammeCodes() {
return function (dispatch) {
dispatch(requestProgrammeCodes())
return api.fetchProgrammes()
.then(
response => response,
error => console.log('An error occurred.', error)
)
.then(json =>
dispatch(receiveProgrammeCodes(json))
)
}
}
function shouldFetchProgrammeCodes(state) {
const codes = state.programmeCodes.codes.length > 0
if (!codes) {
return true
} else if (codes.isFetching) {
return false
} else {
return codes.didInvalidate
}
}
export function fetchProgrammeCodesIfNeeded() {
return (dispatch, getState) => {
if (shouldFetchProgrammeCodes(getState())) {
// Dispatch a thunk from thunk!
return dispatch(fetchProgrammeCodes())
} else {
return Promise.resolve()
}
}
}
'programmeCodes' is a dependency needed to generate 'programmeColors'.
Id like (with good practice) to be able to request 'programmeColors' in a React component without worrying about whether programmeCodes have been received or not.
How do I write actions for 'programmeColors' to achieve the above?

Cancel axios get request when typing reactjs

I'm creating an autocomplete function, so basically whenever I type on search box it will cancel the previous http get request. I checked the jQuery ui autocomplete and that's what they did. Is that possible in axios if yes how can I implement it. So far here's my code for http get request:
export function autocompleteSearchTest(value){
return axios.get(`https://jqueryui.com/resources/demos/autocomplete/search.php`,{
params: {
q: value
}
})
.then(response => {
return response.data.response;
})
.catch(error => {
const result = error.response;
return Promise.reject(result);
});
}
Here's the screenshot when I type on the search box:
as you can see it doesn't cancel the previous http get request.
Since axios v0.15 you can cancel request:
You can also create a cancel token by passing an executor function to
the CancelToken constructor:
var CancelToken = axios.CancelToken;
var cancel;
axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
// An executor function receives a cancel function as a parameter
cancel = c;
})
});
// cancel the request
cancel();
For more information please have a look Cancellation. And according to your code:
import React from 'react'
import axios from 'axios';
export function autocompleteSearchTest(value) {
if (cancel != undefined) {
cancel();
}
return axios.get(`https://jqueryui.com/resources/demos/autocomplete/search.php`, {
cancelToken: new CancelToken(function executor(c) {
// An executor function receives a cancel function as a parameter
cancel = c;
}),
params: {
q: value
}
})
.then(response => {
return response.data.response;
})
.catch(error => {
const result = error.response;
return Promise.reject(result);
});
}
var CancelToken = axios.CancelToken;
var cancel;
export class AutoComplete extends React.Component {
constructor() {
super()
this.state = {
search: ''
};
this.handleSearchChange = this.handleSearchChange.bind(this);
}
handleSearchChange(event) {
const target = event.target;
const value = target.value;
this.setState({
search: value
});
autocompleteSearchTest(value)
}
render() {
return (
<div className="App-intro Dialog-section">
<h2>AutoComplete</h2>
<div className="form-control">
<label htmlFor="password">Lookup :</label>
<input name="search" type="text" value={this.state.search}
onChange={this.handleSearchChange}
id="password" ref="password" placeholder="Enter line"/>
</div>
</div>
)
}
}
export default AutoComplete;
And here it's
use: react, axios and redux-axios-middleware
user actions:
import axios from 'axios';
import * as type from 'constants/user';
const CancelToken = axios.CancelToken;
let cancel;
export const addUser = (data) => {
if (cancel !== undefined) cancel();
return ({
types: [type.ADD_USER_REQUEST, type.ADD_USER_SUCCESS, type.ADD_USER_FAILURE],
payload: {
request: {
url: '/api/user',
cancelToken: new CancelToken(c => cancel = c),
method: 'POST',
data,
},
},
});
};
You can create such a small helper wrapper and use it anywhere where you need to cancel previous request:
// ../services.js
function createApi(baseURL) {
const axiosInst = axios.create({
baseURL,
});
axiosInst.defaults.headers.common['Content-Type'] = 'application/json';
return (type = 'get') => {
let call = null;
return (url, data, config) => {
if (call) {
call.cancel('Only one request allowed!');
}
call = axios.CancelToken.source();
const extConf = {
cancelToken: call.token,
...config,
};
switch (type) {
case 'request':
return axiosInst[type](extConf);
case 'get':
case 'delete':
case 'head':
return axiosInst[type](url, extConf);
default:
return axiosInst[type](url, data, extConf);
}
};
};
}
export const baseApi = createApi('http://localhost:5000/api/')
And then use it anywhere like this:
import baseApi from '../servises.js';
const fetchItemsService = baseApi('post');
//...
componentDidMount() {
fetchItemsService('/items').then(() => {});
}
//...
You wanna use something like RxJS for this, then you can delay the input if the users search, before the requests are made.
use faxios
// building..
let req = faxios()
.baseURL("http://jsonplaceholder.typicode.com")
.url("posts", 1, "comments")
// fetching..
req // =>
.FETCH // => Promise
.then(res => {})
.catch(err => {});
// canceling...
req.cancel();
Based on your code, you can cancel token using axios with this
export function autocompleteSearchTest(value){
const cancelToken = axios.CancelToken.source()// 1st
return axios.get(`https://jqueryui.com/resources/demos/autocomplete/search.php`,{
params: {
q: value
}
})
.then(response => {
return response.data.response;
})
.catch(error => {
if (axios.isCancel(e)) return //3rd
const result = error.response;
return Promise.reject(result);
});
return () =>{ // 2nd
cancelToken.cancel();
}
}

Resources