unable to post values to api using axios in redux - redux

I am trying to post data from a redux-form to an api server using axios.My action creator for posting the data looks like this:
//Action creator for submitting edited post
export function submitEditedPost(values, callback) {
const request = axios.post(`${API}/posts`, {headers}, {values});
return dispatch => {
return request.then((data) => {
callback();
console.log(data)
dispatch({
type:SUBMIT_POST,
payload: data
})
})
}
}
My onSubmit() methos of the form where the action-creator is called looks like this:
onSubmit(values) {
var id = Math.random().toString(36).substr(-8);
var d = new Date().toLocaleTimeString();
const formData = {};
for (const field in this.refs) {
formData[field] = this.refs[field].value;
}
formData.id = id;
formData.timestamp = d;
console.log('-->', formData);
this.props.submitEditedPost(formData, () => {
this.props.history.push('/');
});
}
When I try to console.log the edited values,I can see it correctly,but I am not able to post the edited values and update the api. The error message is shown in the screenshot below:
How do I proceed? Can someone please help me with this issue?
EDIT 1: My whole action file:
import axios from 'axios';
export const FETCH_POSTS = 'fetch_posts';
export const CREATE_POST = 'create_post';
export const FETCH_POST = 'fetch_post';
export const DELETE_POST ='delete_post';
export const EDIT_POST = 'edit_post';
export const SUBMIT_POST = 'submit_post';
let token ;
if(!token)
token = localStorage.token = Math.random().toString(36).substr(-8)
const API = 'http://localhost:3001';
const headers = {
'Accept' : 'application/json',
'Authorization' :'token'
}
//Action creator for fetching posts from the API server
export function fetchPosts() {
const URL = `${API}/posts`;
const request = axios.get(URL,{headers});
return dispatch => {
return request.then(({data}) => {
console.log(data);
dispatch({
type : FETCH_POSTS,
payload : data
})
})
}
}
//Action Creator for creating posts
export function createPosts(values, callback) {
return dispatch => {
return axios.post(`${API}/posts`,values,{headers})
.then((data) => {
callback();
console.log(data)
dispatch({
type: CREATE_POST,
payload: data
})
})
}
}
//Action Creator for displaying a selected post
export function fetchPost(id) {
const request = axios.get(`${API}/posts/${id}`,{headers});
return dispatch => {
return request.then(({data}) => {
console.log(data);
dispatch({
type: FETCH_POST,
payload: data
})
})
}
}
//Action creator for deleting post
export function deletePost(id, callback) {
const request = axios.delete(`${API}/posts/${id}`, {headers})
.then(() => callback());
return {
type: DELETE_POST,
payload: id
}
}
//Action creator for editing post
export function editPost(id, callback) {
const request = axios.get(`${API}/posts/${id}`,{headers});
return dispatch => {
return request.then((data) => {
callback();
console.log(data);
dispatch({
type: EDIT_POST,
payload: data
})
})
}
}
//Action creator for submitting edited post
export function submitEditedPost(id, values, callback) {
console.log(values, 'values')
console.log(id, 'id')
const request = axios.put(`${API}/posts/${id}`, {values}, {headers});
return dispatch => {
return request.then((res) => {
callback();
console.log("response", res)
dispatch({
type:SUBMIT_POST,
payload: res
})
})
}
}

Related

Solution to prefetch in a component on nextjs

I'm looking for a solution/module where I don't need to inject inital/fallback data for swr/react-query things from getServerSideProps. Like...
from
// fetcher.ts
export default fetcher = async (url: string) => {
return await fetch(url)
.then(res => res.json())
}
// getUserData.ts
export default function getUserData() {
return fetcher('/api')
}
// index.tsx
const Page = (props: {
// I know this typing doesn't work, only to deliver my intention
userData: Awaited<ReturnType<typeof getServerSideProps>>['props']
}) => {
const { data } = useSWR('/api', fetcher, {
fallbackData: props.userData,
})
// ...SSR with data...
}
export const getServerSideProps = async (ctx: ...) => {
const userData = await getUserData()
return {
props: {
userData,
},
}
}
to
// useUserData.ts
const fetcher = async (url: string) => {
return await fetch(url)
.then(res => res.json())
};
const url = '/api';
function useUserData() {
let fallbackData: Awaited<ReturnType<typeof fetcher>>;
if (typeof window === 'undefined') {
fallbackData = await fetcher(url);
}
const data = useSWR(
url,
fetcher,
{
fallbackData: fallbackData!,
}
);
return data;
}
// index.tsx
const Page = () => {
const data = useUserData()
// ...SSR with data...
}
My goal is making things related to userData modularized into a component.

How to avoid infinite loop in saga?

I cant understand why the code above runs infinite loop. My actions are different. Could u please take a look?
import { call, put, takeLatest ,delay} from 'redux-saga/effects'
import { saveFetchedDevices,fetchDevicesRequest } from './devicesRedux'
import axios from "axios"
const getDevices= () => {
return axios.get("http://localhost:3131/devices")
}
function* fetchDevicesHandler(action) {
try {
const response = yield call(getDevices);
yield delay(3000);
yield put(saveFetchedDevices(response.data));
} catch (e) {
console.log(e)
}
}
function* mySaga() {
yield takeLatest(fetchDevicesRequest,fetchDevicesHandler);
}
export default mySaga;
fetchDevicesRequest is dispatched from button. Then loop starts
delay is just to slow down infinite loop
this is my redux file
const createActionName = function(name) {
return `app/devices/${name}`
}
const ADD_DEVICE = createActionName("ADD_DEVICE");
const UPDATE_DEVICE = createActionName("UPDATE_DEVICE");
const REMOVE_DEVICE = createActionName("REMOVE_DEVICE");
const SAVE_FETCHED_DEVICES = createActionName("SAVE_FETCHED_DEVICES");
const FETCH_DEVICES_REQUEST= createActionName("FETCH_DEVICES_REQUEST");
//action creators
export const addDevice = payload => ({ type: ADD_DEVICE, payload })
export const updateDevice = payload => ({ type: UPDATE_DEVICE, payload })
export const removeDevice = payload => ({ type: REMOVE_DEVICE, payload })
export const saveFetchedDevices = payload => ({ type: SAVE_FETCHED_DEVICES, payload })
export const fetchDevicesRequest = payload => ({ type: FETCH_DEVICES_REQUEST })
//selectors
export const getAllDevices = state => state.devices.data;
const reducer = function(statePart = [], action = {}) {
switch(action.type) {
case SAVE_FETCHED_DEVICES:
return { data: action.payload }
case ADD_DEVICE:
return { ...statePart, data: [ ...statePart.data, action.payload ] }
case UPDATE_DEVICE:
return { ...statePart, data: [ ...statePart.data.map((device)=>device.id===action.payload.id?action.payload:device)] }
case REMOVE_DEVICE:
return { ...statePart, data: [ ...statePart.data.filter((device)=>device.id!==action.payload.id)] }
default:
return statePart
}
}
export default reducer

Redux State is set also to other State that is not Related

Programming is weird, if you think not then check this case 🤣, I'm using createSlices as Redux and I have two slices with their own states.
First one is orderSlice:
export const orderSlice = createSlice({
name: 'order',
initialState: {
order: null,
message: null,
isLoading: true,
}
})
While the second slice is ordersSlice:
export const orderSlice = createSlice({
name: 'orders',
initialState: {
orders: null,
message: null,
isLoading: true,
}
})
And I have this method to fetch the order and the fulfilled phase where the state is set from:
Fetching the order:
export const fetchOrder = createAsyncThunk('', async ({ token, id }) => {
const requestOptions = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer ' + token,
},
};
try {
const response = await fetch(`${api}/orders/view/${id}`, requestOptions);
const data = await response.json();
return data;
} catch (error) {
console.log(error);
}
});
Filling the order state:
extraReducers: {
[fetchOrder.fulfilled]: (state, action) => {
state.order = action.payload.data;
state.message = 'Succesfully fetched the Order.';
state.isLoading = false;
}
}
While here is method for fetching the orders:
export const fetchAllOrders = createAsyncThunk('', async (token) => {
const requestOptions = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer ' + token,
},
};
try {
const response = await fetch(`${api}/orders/all`, requestOptions);
const data = await response.json();
return data;
} catch (error) {
console.log(error);
}
});
And here updating the orders state:
extraReducers: {
[fetchAllOrders.fulfilled]: (state, action) => {
state.orders = action.payload.data;
state.message = 'Succesfully fetched all Orders.';
state.isLoading = false;
}
}
So the case is that I'm calling the fetchAllOrders in the Order page with UseEffect, here is how:
import { fetchAllOrders } from '../redux/ordersSlice';
useEffect(() => dispatch(fetchAllOrders(user.token)), [user]);
So this is how i run the method to fetch orders with dispatch and it works. But the problem is that when I run this function beside the orders state that is filled with the same data, also the order state is filled with the same data and this is impossible as I've cheked all the cases where I could misstyped a user,users typo but there is none I found, and I don't know.
And here is the store:
import orderSlice from './redux/orderSlice';
import ordersSlice from './redux/ordersSlice';
const store = configureStore({
reducer: {
order: orderSlice,
orders: ordersSlice
},
});
You have to give your thunks an unique name: If you name both '' they will be handled interchangably.
Also, you should be using the builder notation for extraReducers. We will deprecate the object notation you are using soon.

multiple dispatch in redux action

I wanted to dispatch an action from another action but not able to do so. When I try to do so it not able to found getAllUser method.
Below is my action class.
export const myActions = {
getAllUser() {
return (dispatch) => {
makeApiCall()
.then((response) => {
dispatch({
type: USER_SUCCESS,
payload: response,
});
})
.catch((error) => {
dispatch({
type: USER_FAILURE,
payload: error,
});
});
};
},
addUser(user) {
return (dispatch) => {
makeApiCall(user)
.then((response) => {
/*
Need help here :
wants to call above getAllUser()
.then(() =>
dispatch({
type: ADD_SUCCESS,
payload: response,
});
)
*/
};
},
};
I have tried various approaches like,
myActions.getAllUser()
.then((response) =>
dispatch({
type: ADD_SUCCESS,
payload: response,
});
);
and trying do dispatch directly,
const self = this;
dispatch(self.getAllUser());
dispatch({
type: ADD_SUCCESS,
payload: response,
});
One more way around this is after addUser success, update the reducer and than from UI call getAccount again to refresh the results, but just curious to know on how can I achieve this using multiple dispatch.
You can export the functions individually instead of wrapping it under the same object:
export const getAllUser = () => dispatch => { ... }
export const addUser = () => dispatch => {
...
dispatch(getAllUser());
}
You can still import them all if desired:
import * as myActions from '...';
Or you can declare getAllUser first then add to myActions, but the above solution is much cleaner.
const getAllUser = ...
const myActions = {
getAllUser,
addUser = ... { dispatch(getAllUser()) }
}

redux observable: Why don`t can get all actions in test

I'm trying to test a 'redux observable epic' but the test fail because not all actions are in store.getActions() the strange is the store.dispatch function runs.
Epic and actions
export const VERIFY_SESION = 'auth/VERIFY_SESION';
export const SET_POLICIES_ACCEPTED = 'auth/SET_POLICIES_ACCEPTED';
export const AUTHENTICATE = 'auth/AUTHENTICATE';
export function setPoliciesAccepted(wereAccepted: boolean) {
return {
wereAccepted,
type: SET_POLICIES_ACCEPTED,
};
}
export function verifySesion() {
return {
type: VERIFY_SESION,
};
}
export function authenticate(token) {
return {
token,
type: AUTHENTICATE,
};
}
export function verifySesionEpic(action$, store) {
return action$
.ofType(VERIFY_SESION)
.switchMap(async () => {
try {
store.dispatch(setBlockLoading(true));
const token = await AsyncStorage.getItem('token');
if (token !== null) {
store.dispatch(setBlockLoading(false));
return authenticate(token);
}
const policiesWereAccepted = await AsyncStorage.getItem('policiesWereAccepted');
store.dispatch(setBlockLoading(false));
return setPoliciesAccepted(policiesWereAccepted);
} catch (error) {
return setMessage(error.message);
}
});
}
test
describe('actions/auth', () => {
let store;
const asyncStorageGetStub = stub(AsyncStorage, 'getItem');
beforeEach(() => {
store = mockStore();
});
afterEach(() => {
asyncStorageGetStub.restore();
});
it('Should call authenticate if token', () => {
const token = 'mitoken';
asyncStorageGetStub.withArgs('token').returns(Promise.resolve(token));
store.dispatch(verifySesion());
expect(store.getActions()).toContain({ type: AUTHENTICATE, token });
});
});
Test result
1) "actions/auth Should call epic for verifySesion:
Error: Expected [ { type: 'auth/VERIFY_SESION' } ] to include { token: 'mitoken', type: 'auth/AUTHENTICATE' }"
Note
im sure that the conditional token !== null pass
I was to add a timeout before getAction because the 'AUTHENTICATE' actions is added after.
it('Should call authenticate if token', (done) => {
const token = 'mitoken';
asyncStorageGetStub.withArgs('token').returns(Promise.resolve(token));
store.dispatch(verifySesion());
setTimeout(() => {
expect(store.getActions()).toContain({ type: AUTHENTICATE, token });
done();
}, 1000);
});

Resources