Currently I have a redux actions which is getUsers() to get all users.
export function getUsers (params) {
return async dispatch => {
await dispatch({ type: 'GET_USERS_REQUEST' });
const urlParams = params ? new URLSearchParams(Object.entries(params)) : null;
return axios({
method: 'get',
url: Environment.GET_USERS+ `?${urlParams}`,
headers: { 'Content-Type': 'application/json' },
}).then (async res => {
await dispatch({ type: 'GET_USERS_SUCCESS', allUsers: res.data.Data });
}).catch (err => {
dispatch({ type: 'GET_USERS_FAILURE', error: err.toString() });
});
}
}
Now I want to use the same getUsers() but with param id (eg. getUsers({ UserId: 'jamesbond007' })),
and in the action I want to dispatch({ type: 'GET_USER_BY_ID_SUCCESS', user: res.data.Data })
How can I dispatch different actions with the same api call? Should I duplicate the same code but change the action dispatch? If doing so it becomes repetitive function.
You can decide the action by param id, like
// all users, paramId is null
// a user, paramId is xxxx
const action = paramId ?
{ type: 'GET_USER_BY_ID_SUCCESS', user: res.data.Data } :
{ type: 'GET_USERS_SUCCESS', allUsers: res.data.Data };
dispatch(action);
But, I think your idea is not good. It is better to do the logic in 2 methods. It is readable.
I found way to do this. I declare variables REQUEST, SUCCESS, FAILURE and check if the param object contains UserId. If yes, it will dispatch GET_USER_BY_ID instead of GET_USERS.
export function getUsers (params) {
let REQUEST, SUCCESS, FAILURE;
if (!_.isEmpty(params) && params.UserId) {
REQUEST = 'GET_USER_BY_ID_REQUEST';
SUCCESS = 'GET_USER_BY_ID_SUCCESS';
FAILURE = 'GET_USER_BY_ID_FAILURE';
} else {
REQUEST = 'GET_USERS_REQUEST';
SUCCESS = 'GET_USERS_SUCCESS';
FAILURE = 'GET_USERS_FAILURE';
}
return async dispatch => {
await dispatch({ type: REQUEST });
const urlParams = params ? new URLSearchParams(Object.entries(params)) : null;
return axios({
method: 'get',
url: Environment.GET_USERS+ `?${urlParams}`,
headers: { 'Content-Type': 'application/json' },
}).then (async res => {
await dispatch({ type: SUCCESS, payload: res.data.Data });
}).catch (err => {
dispatch({ type: FAILURE, error: err.toString() });
});
}
}
Note that in my reducer, I use only action.payload. Eg:
case 'GET_USERS_SUCCESS':
return { ...state, isFetching: false, userList: action.payload };
case 'GET_USER_BY_ID_SUCCESS':
return { ...state, isFetching: false, user: action.payload };
Related
I have a simple auth stack as follow
export default () => {
const { state } = useContext(AuthContext);
return (
<AuthProvider>
<NavigationContainer>
{state.token ? <MainNavigator /> : <AuthNavigator />}
</NavigationContainer>
</AuthProvider>
);
};
The initial state of token is defined as null in the AuthContext folder, code below. But when running the program i get the following error TypeError: undefined is not an object (evaluating '_useContext.state')
const authReducer = (state, action) => {
switch (action.type) {
case "error":
return { ...state, errorMessage: action.payload };
case "signin":
return { errorMessage: "", token: action.payload };
default:
return state;
}
};
const tokencheck = (dispatch) => async () => {
const token = await AsyncStorage.getItem("token");
if (token) {
dispatch({ type: signin, payload: token });
navigate("Home");
} else {
navigate("SignIn");
}
};
const signup =
(dispatch) =>
async ({ username, password }) => {
try {
const response = await tracker({
method: "post",
url: "/user",
data: qs.stringify({
username: username,
password: password,
}),
headers: {
"content-type": "application/x-www-form-urlencoded;charset=utf-8",
},
});
await AsyncStorage.setItem("token", response.data.email);
// dispatch({ type: "signin", payload: response.data.access_token });
navigate("SignIn");
} catch (err) {
dispatch({
type: "error",
payload: "Something's not write, plz try again",
});
console.log(err);
}
};
const signin =
(dispatch) =>
async ({ username, password }) => {
try {
const response = await tracker({
method: "post",
url: "/login",
data: qs.stringify({
username: username,
password: password,
}),
headers: {
"content-type": "application/x-www-form-urlencoded;charset=utf-8",
},
});
await AsyncStorage.setItem("token", response.data.access_token);
dispatch({ type: "signin", payload: response.data.access_token });
navigate("Home");
} catch (err) {
console.log(err);
dispatch({
type: "error",
payload: "Start debuggin",
});
}
};
const signout = (dispatch) => {
return () => {};
};
export const { Provider, Context } = creatingContext(
authReducer,
{ signin, signout, signup, tokencheck },
{ token: null, errorMessage: "" }
);
The ternary logic is sound and I have defined the initial state then why is this error persisting.
While I was looking for state in token what i should look for is value.
The problem is simply solved with
{state.token != null : <nav/>?<auth/>}
I'm trying to delete an user, but the problem is that I'm trying to do that by taking the user id from the req.body in the database, I have the following logic in the redux actions and I think here is a problem with the order of the params:
export const deleteUser = (userId) => async (dispatch, getState) => {
dispatch({ type: USER_DELETE_REQUEST, payload: userId });
const {
userSignin: { userInfo },
} = getState();
try {
const { data } = await Axios.delete(
"http://localhost:3030/v1/user/userProfile/deleteUser",
{
userId
},
{
headers: { Authorization: `Bearer ${userInfo.token}` },
});
dispatch({ type: USER_DELETE_SUCCESS, payload: data });
} catch (error) {
const message =
error.response && error.response.data.message
? error.response.data.message
: error.message;
dispatch({ type: USER_DELETE_FAIL, payload: message });
}
};
When I'm doing console logging the req.body in the backend is empty, what can I do?
I'm trying to delete an user, but the req.body in the backend is an empty object.
In the backend I have the following code:
const deleteUser = async (req, res) => {
console.log(req.body);
console.log(req.config);
const user = await User.findById(req.body.userId);
if (user) {
const deleteUser = await user.remove();
res.send({ message: "User Deleted", user: deleteUser });
} else {
res.status(404).send({ message: "User Not Found" });
}
};
Here the console log is an empty object, I must that the other functions work perfectly.
In the frontend, I'm using redux, I think I'm doing something wrong in the actions, but I can't find out what, I will post all my code for reference.
action.js:
export const deleteUser = (userId) => async (dispatch, getState) => {
dispatch({ type: USER_DELETE_REQUEST, payload: userId });
try {
const { data } = await Axios.delete(
"http://localhost:3030/v1/user/userProfile/deleteUser",
{
userId: userId,
}
);
dispatch({ type: USER_DELETE_SUCCESS, payload: data });
} catch (error) {
const message =
error.response && error.response.data.message
? error.response.data.message
: error.message;
dispatch({ type: USER_DELETE_FAIL, payload: message });
}
};
In the reducer:
export const userDeleteReducer = (state = {}, action) => {
switch (action.type) {
case USER_DELETE_REQUEST:
return { loading: true };
case USER_DELETE_SUCCESS:
return { loading: false, success: true };
case USER_DELETE_FAIL:
return { loading: false, error: action.payload };
default:
return state;
}
};
And I'm calling the action like that:
const userSignin = useSelector((state) => state.userSignin);
const { userInfo, loading, error } = userSignin;
<button
onClick={() => {
console.log(userInfo._id);
dispatch(deleteUser(userInfo._id));
props.onClose();
}}
className='deleteAccountModalButton'
>
Delete account!
</button>
I tried everything, but I can't find where the problem, can somebody tell me why the req.body is empty in the backend?
EDIT:
I managed to make it work by modifying the order of parameters in actions:
export const deleteUser = (userId) => async (dispatch, getState) => {
dispatch({ type: USER_DELETE_REQUEST, payload: userId });
const {
userSignin: { userInfo },
} = getState();
try {
const { data } = await Axios.delete(
"http://localhost:3030/v1/user/userProfile/deleteUser",
{
data: {
headers: { Authorization: `Bearer ${userInfo.token}` },
userId,
},
}
);
dispatch({ type: USER_DELETE_SUCCESS, payload: data });
} catch (error) {
const message =
error.response && error.response.data.message
? error.response.data.message
: error.message;
dispatch({ type: USER_DELETE_FAIL, payload: message });
}
};
I will leave this here in case somebody else will have this problem.
I am doing some nested axios calls as in order to create the object i want i need to fire off a couple of different API's. My problem is that when i call setState, the state will start updating before all requests have finished so my table will populate entry by entry which does not look nice.
here is my code:
fetchServices = async ()=> {
this.setState({isLoading: true})
await axios({
method: 'get',
url :getApiUrl(),
headers:{
"Accept": "application/json"
}
})
.then( async response => {
let Data: any[] = []
response.data.Message.forEach(async (e: any) => {
await axios({
method: 'get',
url: getApiUrl() + "/" + e.organisationErn + "/services",
headers:{
"Accept": "application/json"
}
}).then( async response => {
console.log(response)
if(response.data.Message !== "No services found"){
response.data.Message.forEach(async(e:any)=>{
let orgName = await axios({
method: 'get',
url: getApiUrl() + "/" + e.organisationErn,
headers:{"Accept": "application/json"}})
.then(response => {return response.data.Message.organisationName});
let entry = {
servicename: { text: e.serviceName },
servicetype: { text: e.serviceTypeDescription },
organisation: { text: orgName },
};
Data.push(entry);
this.setState({ tableData: Data });
})
};
});
});
setTimeout(()=>{this.setState({isLoading: false})}, 100)
}).catch(error => {
alert(error)
});
};
Well you are pushing your data one by one. You can use Promise.all to await all requests complete then populate your state.
if (response.data.Message !== "No services found") {
const promises = response.data.Message.map((e: any) => {
axios({
method: 'get',
url: getApiUrl() + "/" + e.organisationErn,
headers: {
"Accept": "application/json"
}
})
.then(response => {
return response.data.Message.organisationName
})
.then(org => {
return {
servicename: {
text: e.serviceName
},
servicetype: {
text: e.serviceTypeDescription
},
organisation: {
text: org
},
};
});
});
const Data = await Promise.all([...promises]);
this.setState({ tableData: Data });
BTW there may be some parentheses errors.
I think its better to get the result from each axios request and use it at the end, instead of using then
like
const result1= await axios.get(...);
const result1= await axios.get(...);
const result1= await axios.get(...);
setState({ tableData: Data });
you can also refer https://medium.com/better-programming/how-to-use-async-await-with-axios-in-react-e07daac2905f
We are trying to redirect to a search results page once we have our results. We are using axios.get() to send the get request which follows:
export const searchPosts = (type, query) => async dispatch => {
dispatch({ type: PARTIAL_LOADING, payload: false });
const res = await axios.get('/api/search/',
{
params: {
type: type,
query: query
}});
dispatch({ type: PARTIAL_LOADING, payload: true });
dispatch({
type: SEARCH,
payload: res.data
});
};
We are trying to redirect to the next page using the following:
async submitSearch(values) {
const type = values.type ? values.type : "Default";
const query = values.query;
if (type && query)
{
await this.props.searchPosts(type, query)
.then(_ => {
this.props.history.push('/search_results');
});
}
}
This redirects after the first attempt but will always fail the first time.