Using local storage with react-redux? - reactjs

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

Related

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

How to Create Middleware for refresh token in Reactjs with axios and redux

i am working with reactjs on front end the issue is after certain time period the accessToken is expired and server send status of 401(unauthorized) then i need to send refresh token back to server it works fine until i manually send the refresh token i set the setInterval function but thats not a good approach how to automatically send it when token is expired.
i also google it but everyone is talking about creating middleware anyone please give me the hint how to create that middleware or any other solution or link any article related to it . i created this but this didnt works for me however when server send status of 401 then middleware ran but it dosent dispatch my refreshToken() function
const customMiddleWare = store => next => action => {
axios.interceptors.response.use(function (response) {
return response;
}, function (error) {
if(error.status === 401) {
// do something when unauthorized
store.dispatch(refreshToken());
}
return Promise.reject(error);
});
console.log("Middleware triggered:", action);
next(action);
}
By the way i am using redux, redux-thunk and axios. thanks,
some time ago i used to use the next way:
First of all i created some api folder, where each function returns data for axios requests
// /api.js
export function signIn (data) {
return {
method: 'post',
api: '/sign-in'
data: data
}
}
export function signUp (data) {
return {
method: 'post',
api: '/registration'
data: data
}
}
then i generated action type by specific rule, like: SIN_IN_REQUEST, where: SIGN_IN means signIn function in /api.js; REQUEST means that you need to do api request. As result my middleware looked like the next:
// request middleware
const instance = axios.create({
baseURL: '/api'
});
function camelize(str) {
return str.replace(/(?:^\w|[A-Z]|\b\w)/g, function(word, index) {
return index === 0 ? word.toLowerCase() : word.toUpperCase();
}).replace(/\s+/g, '');
}
const customMiddleWare = store => next => action => {
if (!action.type.endsWith('_REQUEST')) {
next();
return;
}
const methodName = action.type.replace('_REQUEST', ''); // removed _REQUEST from action type
const camelCaseMethodName = camelize(methodName); // the result is "signIn"
const method = api[camelCaseMethodName];
if (!method) {
next();
return;
}
const dataForRequest = method(action.payload);
try {
const response = await instance(dataForRequest);
const newActionType = action.type.replace('_REQUEST', '_SUCCESS');
dispatch({
type: newActionType,
payload: {
requestPayload: action.payload,
response: response,
}
})
} catch(error) {
if (error.status === '401') {
dispatch(refreshToken());
next();
return;
}
const newActionType = action.type.replace('_REQUEST', '_FAILURE');
dispatch({
type: newActionType,
payload: {
requestPayload: action.payload,
error: error,
}
})
}
next();
}
After that you can easily manage any api request in your application like that:
function someTHunkMethod(username, password) {
return (dispatch, getState) => {
dispatch({
type: 'SIGN_IN_REQUEST',
payload: {
username,
password
}
})
}
}
function oneMoreThunk(data) {
return (dispatch, getState) => {
dispatch({
type: 'GET_USERS_REQUEST',
payload: data
})
}
}
And in reducer do something like that
...
switch (action.type) {
case 'SIGN_REQUEST':
return {
isLoading: true,
user: null
}
case 'SIGN_SUCCESS':
return {
isLoading: false,
user: action.payload.response.data
}
case 'SIGN_FAILURE':
return {
isLoading: false,
user: null
}
default:
return state
}

send get request with id in redux saga

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

How to put fetch function in separate file? [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
So I want to have fetch request in separate file. Just for testing. And then call it in componentDidMount. I still get undefined. What is the proper way of doing it?
This is my function:
const fetchNewWord = () => {
fetch("https://wordsapiv1.p.rapidapi.com/words/?lettersMax=11&random=true", {
headers: {
"X-Rapidapi-Host": "wordsapiv1.p.rapidapi.com",
"X-Rapidapi-Key": "myKey"
}
})
.then(data => data.json())
.then(data => {
return data.word;
});
};
export default fetchNewWord;
You have return your fetch callback results as function's response:
export const fetchNewWord = () => {
return fetch("https://wordsapiv1.p.rapidapi.com/words/?lettersMax=11&random=true", {
headers: {
"X-Rapidapi-Host": "wordsapiv1.p.rapidapi.com",
"X-Rapidapi-Key": "myKey"
}
})
.then(data => data.json())
.then(data => data.word);
};
When you do const data = fetchNewWord(); console.log(data);, you'll see the result.
You can create a separate service for fetch in your code and use it as a independent providers
Here is the httpRequest.js which you can use default fetch api:
import axios from 'axios';
class HttpRequest {
constructor(baseURL) {
this.axios = axios.create({
baseURL,
});
}
reponseInterceptor() {
// Add a response interceptor
this.axios.interceptors.response.use(
response => (
// Do something with response data
response
),
error => (
// Do something with response error
Promise.reject(error)
),
);
}
requsetInterceptor() {
this.axios.interceptors.request.use(
config => (
// Do something before request is sent
config),
error => (
// Do something with request error
Promise.reject(error)
),
);
}
fetch(url, params, config = {}) {
return this.axios.get(url, {
params,
...config,
});
}
create(url, data, config = {}) {
return this.axios.post(url, data, {
...config,
});
}
update(url, data, config = {}) {
return this.axios.put(url, data, {
...config,
});
}
patch(url, data, config = {}) {
return this.axios.patch(url, data, {
...config,
});
}
remove(url, params, config = {}) {
return this.axios.delete(url, {
params,
...config,
});
}
}
export default HttpRequest;
Here is how you can create your words.js services:
import config from 'config';
import HttpRequest from './httpRequest';
export default class WordService extends HttpRequest {
constructor(servicePath) {
super(config.markMeHost);
this.path = `${config.markMeHost}/${servicePath}`;
}
listWords() {
return this.fetch(this.path, {});
}
createWords(data) {
return this.create(this.path, data);
}
updateWords(data, id) {
return this.update(`${this.path}/${id}`, data);
}
deleteWords(id) {
return this.remove(`${this.path}/${id}`);
}
}
Your api service index.js:
import WordService from './words';
// Give arg to provider to start endpoint with specific path for example = abc.com/api/words
export default new WordService('words');
For further details you can check my github account for axios service https://github.com/m-nathani/markme/tree/master/frontend/src/service/api

cant .then Promise in react / redux / thunk

Completely new to JS, react, redux and thunk all together.
I am fetching data from an endpoint and I want to site to load / display an error if the fetch was resolved or rejected, but somehow I cant call .then on the fetch I return in my actioncreator.
//projectApi.js
function add(project){
const requestOptions = {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(
project
)
};
return fetch(config.apiUrl + "/projects", requestOptions).then(handleResponse);
}
function handleResponse(response) {
return new Promise((resolve, reject) => {
if (response.ok) {
var contentType = response.headers.get("content-type");
if (contentType && contentType.includes("application/json")) {
response.json().then(json => resolve(json));
} else {
resolve();
}
} else {
response.json().then(json => reject(json));
}
});
}
Then in my ActionCreator I'm doing this:
//projectActions.js
function add(project){
return dispatch => {
dispatch(request());
return projectApi.add(project)
.then( project => {
dispatch(success(project));
},
error => {
dispatch(failure(error));
}
);
};
function request() {
// left out for brevity
}
function success(project) {
// left out for brevity
}
function failure(error) {
// left out for brevity
}
}
However, if I now try to .then my dispatch...
//SomePage.js
handleSubmit(e) {
e.preventDefault();
var project = { name: this.state.projectName };
this.props.add(project).then(...);
}
...
function mapDispatchToProps(dispatch) {
return {
add: (project) => {
dispatch(projectActions.add(project));
}
};
}
I get "TypeError: this.props.add(...) is undefined", however all the actions are properly dispatched. (e.g. request, failure, success) and the store is updated.
Sorry if its a really stupid mistake.

Resources