I am working on a calculator using MERN stack but my client and server are different projects. React App is running on 3030 and Node Backend on 3000. I am able to retrieve the correct response from Node Backend, but not able to update it to the store, mostly due to the issue with the scope of 'state' or returned data. Below is my code snippet :
const calcReducer = (state = calcState, action) => {
switch(action.type){
case 'ADD_ELEM':
return {
...state,
value: state.value == 0 ? action.text : state.value + action.text
}
case 'CLEAR':
return{
...state,
value: 0
}
case 'EQUAL':
const url = 'http://localhost:3000/calculate';
superagent
.post(url)
.send({ exp : state.value })
.set('Accept', 'application/json')
.end((err,data) => {
if (err)
return state;
else {
console.log(state); //prints the old value of the state
//below prints the correct state value but returning the state from here doesn't work
console.log({
...state,
value : Number(JSON.parse(data.text).result)
})
}
})
return {
...state,
value : VALUE // how can the value be brought here from inside of else loop
}
default:
return state;
}
}
console.log statement inside 'else' prints correctly but no effect if I return state value from there. The place from where I am currently returning 'state' is not working out for me, and the returned state is exactly same as the state before the control came inside the case. Can someone please explain me how to work with the scope as I am new to ES6?
Edit1:
When I try to take the 'async-ness' out of the reducer, and make change as given below:
const mapStateToProps = (state) => {
return{
value: state.value,
btns: state.btns
}
}
const mapDispatchToProps = (dispatch) => {
return{
addElem: (text) => {
dispatch({
type: 'ADD_ELEM',
text
})
},
clear: () => {
dispatch({
type: 'CLEAR'
})
},
equal: (value) => {
console.log(value)
superagent
.post('http://localhost:3000/calculate')
.send({ exp : value })
.set('Accept', 'application/json'))
.end((err,data) => {
dispatch({ type: 'EQUAL', JSON.parse(data.text).result })
})
}
}
}
In this case, code build fails saying:
Module build failed: SyntaxError: Unexpected token (74:2)
72 |
73 | const mapStateToProps = (state) => {
> 74 | return{
| ^
75 | value: state.value,
76 | btns: state.btns
77 | }
There are syntax errors in your mapDispatchToProps, try to well indent your code so it will be more easy to identify them.
const mapDispatchToProps = (dispatch) => {
return {
addElem: (text) => {
dispatch({
type: 'ADD_ELEM',
text
})
},
clear: () => {
dispatch({
type: 'CLEAR'
})
},
equal: (value) => {
console.log(value)
superagent
.post('http://localhost:3000/calculate')
.send({ exp : value })
.set('Accept', 'application/json')
.end((err,data) => {
dispatch({ type: EQUAL, result: JSON.parse(data.text).result })
})
}
};
};
The return statement is within a callback function. So, you are returning only from the callback function. You probably should wrap it in a Promise as follows.
return new Promise((resolve, reject) => {
superagent(...)
...
.end((err, data) => {
if (err)
reject(state);
else {
resolve({
...state,
value : Number(JSON.parse(data.text).result)
});
}
});
});
Related
any idea why my cloud function is telling my that (Not all code paths return a value) I put a return on the promise at the end also on the if statement yet it is not working, I read about similar issues tried to solve according to them but I still missing part of the puzzle, the error started showing when I wrapped the whole thing with try-catch.
export const createClient = functions.https.onCall(async(data, context) => {
try {
const snapshotCheckBelong = await db.collection('data- main').doc(data.sharedId).get();
const getClient = snapshotCheckBelong.data() ? .clients.filter((el: any) => el.clientId === data.clientId);
const snapshotCheckExist = await db.collection('data-registered-clients').doc(data.sharedId).get();
const checkClient = snapshotCheckExist.data() ? .clients.filter((el: any) => el.clientId === data.clientId);
if (checkClient[0]) {
return {
result: 'Registered client already exisits.'
};
}
if (getClient[0] ? .clientId === data.clientId && checkClient.length === 0) {
const payload = {
user: 'client',
verifiedEmail: false,
createdAt: admin.firestore.Timestamp.now(),
};
let auth = admin
.auth()
.getUserByEmail(context.auth ? .token ? .email!)
.then((user) =>
admin.auth().setCustomUserClaims(user.uid, {
userType: 'client',
premiumUnitll: admin.firestore.Timestamp.fromDate(snapshotCheckBelong.data() ? .premiumUntill)
})
)
.catch((err: any) => {
console.log(err);
});
let setClientData = db
.collection('data-clients')
.doc(context.auth ? .uid!)
.set(payload)
.catch((err: any) => {
console.log(err);
});
let updateRegisteredClients = db
.collection('data-registered-clients')
.doc(data.sharedId)
.update({
clients: admin.firestore.FieldValue.arrayUnion({
clientAccount: context.auth ? .uid,
clientId: data.clientId,
trainerId: data.sharedId
})
})
.catch((err: any) => {
console.log(err);
});
return Promise.all([auth, setClientData, updateRegisteredClients]);
} else {
return null;
}
} catch {
(err: any) => {
console.log(err);
};
}
});
Your catch blocks are not returning anything. If you handle an error with catch, you have to determine what value gets returned to the caller.
I typically prefer sending the error back to the caller, as shown in the documentation on handling errors in callable cloud functions and on the calling client.
I have been able to pull data from an API that I built using MongoDB and Express, but am having trouble rendering the nested data to my React component.
For example, if I type in <p>{restaurant.cuisine}</p> I am able to retrieve Burgers, American, but if I try and access {restaurant.status.delivery}, I get an error that says:
Cannot read property 'delivery' of undefined.
But if I {console.log(restaurant.status} I can see the object? I tried turning the object into an array using Object.values, but that didn't work either.
The same thing happens if I try to access the nested objects in {restaurant.images} and {restaurant.geometry}.
Here's a copy of my React hook:
import { useReducer, useEffect } from 'react';
import axios from 'axios';
const ACTIONS = {
MAKE_REQUEST: 'make-request',
GET_DATA: 'get-data',
ERROR: 'error',
};
function reducer(state, action) {
switch (action.type) {
case ACTIONS.MAKE_REQUEST:
return { loading: true, restaurant: [] };
case ACTIONS.GET_DATA:
return {
...state,
loading: false,
restaurant: action.payload.restaurant,
};
case ACTIONS.ERROR:
return {
...state,
loading: false,
error: action.payload.error,
restaurant: [],
};
default:
return state;
}
}
export default function useFetchSingleRestaurant({ id }) {
const [state, dispatch] = useReducer(reducer, {
restaurant: [],
loading: true,
});
useEffect(() => {
dispatch({ type: ACTIONS.MAKE_REQUEST });
axios
.get('http://localhost:4444/restaurants/' + id)
.then((res) => {
dispatch({
type: ACTIONS.GET_DATA,
payload: { restaurant: res.data.restaurant },
});
})
.catch((e) => {
dispatch({
type: ACTIONS.ERROR,
payload: { error: e },
});
});
}, [id]);
return state;
}
I'm accessing it in my SingleRestaurant component:
function SingleRestaurant({ match }) {
const { restaurant } = useFetchSingleRestaurant({ id: match.params.id });
return (
<p>{restaurant.status.delivery}</p>
)
}
And then here's my backend setup as well:
showRestaurant = async (req, res) => {
const restaurant = await Restaurant.findById(req.params.id)
.populate({ path: 'reviews', populate: { path: 'author' } })
.populate('author');
if (!restaurant) {
req.flash('error', 'Restaurant not found.');
return res.redirect('/restaurants');
}
res.send({ restaurant });
};
Until your server request returns restaurant it will be set as the default [] that you have set.
An empty array does not have a property of status, so hence the error.
if you change your default to null:
const [state, dispatch] = useReducer(reducer, {
restaurant: null,
loading: true,
});
And then check for a value:
function SingleRestaurant({ match }) {
const { restaurant } = useFetchSingleRestaurant({ id: match.params.id });
if (!restaurant) return 'Loading'
return (
<p>{restaurant.status.delivery}</p>
)
}
You could also pass back the loading state from your hook and then do a check on that.
I want show initial data in my formArray
i can set value and show value in console log but dont show this data in the ui form
ngOnInit() {
this.getCertificate(this.id);
this.assessmentForm = this.fb.group({
certificateArray: this.fb.array([ this.createItem() ]),
});
}
createItem(): FormGroup {
return this.fb.group({
confirm: '',
score: '',
description: ''
});
}
getCertificate(id) {
this.certificateList = [];
this.UsersRegisterService.getCertificate(id).subscribe((res: any[]) => {
this.certificateList = res;
var index=0;
this.certificateList.forEach(element => {
this.AssessmentService.getCertificateAssessment(element.id.value).subscribe((res: any[]) => {
if(res!=null){
this.certificateArray.at(index).setValue(
{ confirm: res['confirm'], score: res['score']['value'],description:res['description']});
console.log( this.assessmentForm['controls'].certificateArray['controls'][index]['controls'].score.value);
}
});
index++;
});
});
}
i set value this method
this.certificateArray.at(index).setValue(
{ confirm: res['confirm'], score: res['score']})
please help me how can i show this value in the Ui form
Use patchValue
this.certificateArray.at(index).patchValue(res);
Note that you would never subscribe inside a subscription, and even less often (never never) subscribe in a forEach. pipe the data instead.
You can refactor your code a little bit, right now you have a shadowed name res. I would log responses from each call to getCertificateAssessment to make sure you're getting what you expect:
getCertificate(id) {
this.certificateList = []; // this should be set at the top of your component
this.UsersRegisterService.getCertificate(id).pipe(
catchError(err => {
console.log('get cert error', err);
return [];
})
).subscribe((list) => {
this.certificateList = list;
this.certificateList.forEach((element, i) => {
this.AssessmentService.getCertificateAssessment(element.id.value).pipe(
catchError(err => {
console.log('get assessment error', err);
return null;
})
).subscribe((res) => {
if (res) {
console.log('res', i, res); // be sure of response
this.certificateArray.at(i).setValue({
confirm: res.confirm,
score: res.score.value,
description: res.description
});
} else {
console.log('no res!');
}
});
});
});
}
Chris makes a good point about piping but I'm guessing these service calls are http requests so they complete just like a promise.
I also added catchError to catch errors with the service calls.
Plus you are having to make a call for each assessment which makes for a lot of calls. Maybe refactor your backend to make it 1 call?
If you refactored the endpoint for getCertficateAssessment to accept an array of values and return an array of responses, you could do this:
this.UsersRegisterService.getCertificate(id).pipe(
switchMap(list => this.AssessmentService.getCertificateAssessment(list.map(l => l.id.value)))
);
You would need to create an id for each assessment so you could assign them.
I got list of items from my database mySql and also button 'edit'.
When I clicked edit (by id) I want to see all fields filled by data.
But I only have in my console: undefined
If I tested my api by postman it works fine.
There is how I am getting list.
{
const id = this.actRoute.snapshot.paramMap.get('id');
this.studentApi.GetStudent(id).subscribe((res: any) => {
console.log(res.data);
this.subjectArray = res.data;
console.log(this.subjectArray);
this.studentForm = this.fb.group({
id: [res.id, [Validators.required]],
domain_id: [res.domain_id, [Validators.required]],
source: [res.source, [Validators.required]],
destination: [res.destination]
});
});
}
There is my api.service.ts
GetStudent(id): Observable<any> {
const API_URL = `${this.endpoint}/read-student/${id}`;
return this.http.get(API_URL, { headers: this.headers })
.pipe(
map((res: Response) => {
return res || {};
}),
catchError(this.errorMgmt)
);
}
And there is my route
studentRoute.get('/read-student/:id', (request, response) => {
const id = request.params.id;
con.query('SELECT * FROM students WHERE id = ?', id, (error, result) => {
if (error) throw error;
response.send(result);
});
});
There is response from 'postman'
[
{
"id": 5,
"domain_id": 2,
"source": "tester0700#test.pl",
"destination": "testw#test.pl"
}
]
It seems like the response is an array, containing an object.
In that case, there is no need to use res.data, as that would imply the returned observable, res has a property named data, and that you are trying to access the value within that property. You can simply assign res to the subjectArray property. I am pretty sure res would be defined.
this.studentApi.GetStudent(id).subscribe((res: any) => {
console.log(res);
this.subjectArray = res;
// handle the rest here.
});
I not undestand everything with javascript etc, I want to get my data returned by ma action redux but i'have a problem with my code.
const mapStateToProps = state => {
const group = state.groupReducer.group ? state.groupReducer.group : [ ]
return {
group
}
how i can get my data ?
When I try with that:
const mapStateToProps = state => {
const group = state.groupReducer.group.data.data[0] ? state.groupReducer.group.data.data[0] : [ ]
return {
group
}
And my goal is map around group
renderGroup = group => {
return group.map((groups => {
<div key={groups.data.data.id}>
//
</div>
}))
}
Sagas.js
export function* loadApiDataGroup() {
try {
// API
const response = yield
call(axios.get,'http://localhost:8000/api/group');
yield put(loadGroup(response))
} catch (e) {
console.log('REQUEST FAILED! Could not get group.')
console.log(e)
}
}
Action.js
export function loadGroup(data){ return { type: LOAD_GROUP, data }};
export function creatGroup(data){ return { type: CREATE_GROUP, data}};
// reducer
export default function groupReducer( state= {}, action = {}){
switch (action.type){
case LOAD_GROUP:
return {
...state,
group: action.data
}
case CREATE_GROUP:
return {
...state
}
default:
return state
}
thank you to help me
Try
const mapStateToProps = state => ({
group: state.groupReducer.group || []
});
Then you can use this.props.group in the component. Even though you might only want one thing in mapStateToProps, it's usually not directly returned like that.
If group is the response of an API request, you need to unpack data first, this is done in your async action creator (you will want to use redux-thunk or something similar):
const getGroup = () => async (dispatch) => {
dispatch({ type: 'GET_GROUP_REQUEST' });
try {
const { data } = await axios.get('/some/url');
dispatch({ type: 'GET_GROUP_SUCCESS', payload: data });
} catch (error) {
dispatch({ type: 'GET_GROUP_FAILURE', payload: error });
}
};