Why is not the state updated? - reactjs

I have a function that updates a state with a change and adds a value, but the state in the 'addResponse' function does not always change:
handleSelected (e, item) {
this.setState({
current_component_id: item.id,
}, () => this.addResponse()
);
};
Call function above:
addResponse (e) {
const { enrollment_id, evaluation_id, user_id, question_id, current_component_id,
responses, current_question, current_question_id
} = this.state;
console.log(current_component_id)
if (current_component_id != 0) {
const newResponse = {
enrollment_id: enrollment_id,
evaluation_id: evaluation_id,
user_id: user_id,
question_id: current_question_id,
answer_component: current_component_id,
};
function hasAnswer(res) {
const list_question_id = res.map((item) => {
return item.question_id
});
if (list_question_id.includes(current_question_id)) {
return true
} else {
return false
}
}
if (responses === undefined) {
this.setState({
responses: [newResponse]
}
, () => console.log('---------> primeiro', this.state.responses)
)
} else {
const check = hasAnswer(responses);
if (check) {
this.setState(prevState => {
prevState.responses.map((item, j) => {
if (item.question_id === current_question_id) {
return item.answer_component = current_component_id
}
return item ;
})
}
, () => { console.log('----> questao alterada ', this.state.responses)}
)
} else {
this.setState({
responses: [...this.state.responses, newResponse]
}
, () => console.log('------> questao nova', this.state.responses)
);
}
}
}
// this.nextQuestion();
};
the first console.log is always correct, but the others do not always change, I know that setState is asyn, but I thought that as I call the addResponse function it would be async

There is a problem in your how you call setState when check is true.
It should be
this.setState(prevState => ({
responses: prevState.responses.map((item, j) => {
if (item.question_id === current_question_id) {
item.answer_component = current_component_id
}
return item ;
})
})
, () => { console.log('----> questao alterada ', this.state.responses)}
)

Related

How to add some option to a select box above all mapping in React?

I want to add an All Option to my existing select box.
Select box is creating with some API data. With the API data set I want to add an ALL option above.
This is my code.
const useChemicals = () => {
const [data, setData]: any = useState([]);
useEffect(() => {
const getChemicalsData = async () => {
try {
const results = await searchApi.requestChemicalsList();
if (results.data) {
let groupCount = 0;
const chemList: any = [];
results.data.data.chemicals.map((chemical: any, index: number) => {
if (chemical.key === '') {
chemList.push({
label: chemical.value,
options: [],
});
}
});
results.data.data.chemicals.map((chemical: any, index: number) => {
if (chemical.key === '') {
if (index > 1) {
groupCount += 1;
}
} else {
chemList[groupCount].options.push({
label: chemical.value,
value: chemical.key,
});
}
});
setData([...chemList]);
}
} catch (e) {}
};
getChemicalsData();
}, []);
return data && data;
};
export default useChemicals;
How can I add this. Please help me, I am new to React.

React-Apollo: Recommended way of subscribing to multiple events that doesn't require UI updates

So i want to subscribe to multiple events for the current logged user.
I've extracted the subscriptions to a separate function that update my logged user state from inside and returns an array of subscriptions.
Now i wanted to know is there a different / better way of doing this ?
Is this the correct / recommended way of approaching this problem ?
Current implementation
export const subscribeToCurrentUserUpdates = (setLoggedUser) => {
const friendRequestObserver$ = apolloClient.subscribe(
{ query: queries.NEW_FRIEND_REQUEST },
);
const followersUpdatesObserver$ = apolloClient.subscribe(
{ query: queries.FOLLOWERS_UPDATES },
);
const acceptedFriendRequestObserver$ = apolloClient.subscribe(
{ query: queries.ACCEPTED_FRIEND_REQUEST },
);
const friendRequestSubscription = friendRequestObserver$.subscribe({
next: ({ data: { newFriendRequest } }) => {
Alert.success(`${newFriendRequest.username} just sent you a friend request`);
setLoggedUser((loggedUser) => {
loggedUser.incomingFriendRequests.unshift(newFriendRequest._id);
});
},
error: err => console.error(err),
});
const followersUpdatesSubscription = followersUpdatesObserver$.subscribe({
next: ({ data: { followersUpdates: { follower, isFollow } } }) => {
if (isFollow) {
Alert.success(`${follower.username} is now following you`);
}
setLoggedUser((loggedUser) => {
isFollow
? loggedUser.followers.unshift(follower._id)
: loggedUser.followers.splice(loggedUser.followers.indexOf(follower._id), 1);
});
},
error: err => console.error(err),
});
const acceptedFriendRequestSubscription = acceptedFriendRequestObserver$.subscribe({
next: ({ data: { acceptedFriendRequest: newFriend } }) => {
Alert.success(`${newFriend.username} just accepted your friend request!`);
setLoggedUser((loggedUser) => {
loggedUser.friends.push(newFriend._id);
loggedUser.sentFriendRequests.splice(
loggedUser.sentFriendRequests.indexOf(newFriend._id), 1,
);
});
},
error: err => console.error(err),
});
return [
friendRequestSubscription,
followersUpdatesSubscription,
acceptedFriendRequestSubscription,
];
};
The way i subscribe from my component
const App = () => {
const currentUserSubscriptionRef = useRef();
useEffect(() => {
if (loggedUser && !currentUserSubscriptionRef.current) {
currentUserSubscriptionRef.current = subscribeToCurrentUserUpdates(
setLoggedUser,
);
}
if (!loggedUser && currentUserSubscriptionRef.current) {
currentUserSubscriptionRef.current.forEach((subscription) => {
subscription.unsubscribe();
});
currentUserSubscriptionRef.current = null;
}
}, [loggedUser, setLoggedUser]);
}

Why did splice delete only last element and don't work by key?

deleteProtoItem = (protoKey) => {
return e => {
if (!isUndefined(e)) {
e.preventDefault();
}
const {protocol} = this.state;
modalConfirm(`<div>Вы уверены что хотите удалить эту строку?</div>`, {
cancel: "Не удалять",
ok: "Удалить"
}).then(() => {
protocol.tasks.splice(protoKey, 1);
this.setState({protocol}, () => {
});
}).catch(handleErrorResponse);
};
};
When I delete element don't mind what key number it have 0 or 3, anyway splice remove only last element.
Protocol.tasks array
This might be happening because protoKey is undefined, try passing protoKey to the callback
deleteProtoItem = (protoKey) => {
return e => {
if (!isUndefined(e)) {
e.preventDefault();
}
const {protocol} = this.state;
modalConfirm(`<div>Вы уверены что хотите удалить эту строку?</div>`, {
cancel: "Не удалять",
ok: "Удалить"
}).then((protoKey) => {
protocol.tasks.splice(protoKey, 1);
this.setState({protocol}, () => {
});
}).catch(handleErrorResponse);
};
};

How can I put this function into a component?

I have a react native codebase in which I import a component and use the same function twice but with slight differences. I would like to outsource it into a new component somehow. Any ideas?
It looks like this :
handleConfirmApplication = async () => {
const checkVals =
get('shiftInvite.account.accountName', this.props) === ONBOARDING_ACCOUNT
? omit('payRate', this.props.confirmationCheckValues)
: this.props.confirmationCheckValues;
if (Object.values(checkVals).every(val => val)) {
this.props.onToggleConfirmPopUp();
this.props.onToggleLoadingApply();
try {
await this.handleShiftInviteDecision('ACCEPT')();
} catch (e) {
Alert.alert('Error', parseError(e));
} finally {
this.props.onToggleLoadingApply();
}
} else {
Alert.alert('Error', 'Please confirm all shift requirements');
}
};
And the second one is the following :
handleConfirmApplication = async () => {
const checkVals =
get('shift.account.accountName', this.props) === ONBOARDING_ACCOUNT
? omit('payRate', this.props.confirmationCheckValues)
: this.props.confirmationCheckValues;
if (Object.values(checkVals).every(val => val)) {
this.props.onToggleConfirmPopUp();
this.props.onToggleLoadingApply();
try {
const shiftId = this.props.shift.id;
const {
data: { updatedShifts },
} = await this.props.updateMyApplication(shiftId, 'APPLY');
this.setState({
updatedShift: updatedShifts.find(({ id }) => id === shiftId),
});
} catch (e) {
Alert.alert('Error', parseError(e));
} finally {
this.props.onToggleLoadingApply();
}
} else {
Alert.alert('Error', 'Please confirm all shift requirements');
}
};
Simply use an if/else statement in your try/catch and a ternary condition to create your string. Choosing between one or another should be done by passing a parameter to your function :
handleConfirmApplication = async (isInvite) => {
const checkVals =
get(`shift${isInvite ? 'Invite' : ''}.account.accountName`, this.props) === ONBOARDING_ACCOUNT
? omit('payRate', this.props.confirmationCheckValues)
: this.props.confirmationCheckValues;
if (Object.values(checkVals).every(val => val)) {
this.props.onToggleConfirmPopUp();
this.props.onToggleLoadingApply();
try {
if(isInvite){
await this.handleShiftInviteDecision('ACCEPT')();
}
else{
const shiftId = this.props.shift.id;
const {
data: { updatedShifts },
} = await this.props.updateMyApplication(shiftId, 'APPLY');
this.setState({
updatedShift: updatedShifts.find(({ id }) => id === shiftId),
});
}
} catch (e) {
Alert.alert('Error', parseError(e));
} finally {
this.props.onToggleLoadingApply();
}
} else {
Alert.alert('Error', 'Please confirm all shift requirements');
}
};
And calling it :
handleConfirmApplication(true)
Have I missed any other differences between your functions ?
To Use it in a reusable component :
handleConfirmApplication = async () => {
const { isInvite } = this.props
const checkVals =
And calling it :
<MyComponent isInvite={false} /> //Just switch it to true to get the other version

Concat object to state object

I am trying to manage my local state whilst also updating an API which holds a list of books. In this setup, when the updateShelf method recieves a book and a shelf, it checks to see if book is already in the book state, if not it should concat the book param on the book state. Struggling to work out how to do this.
class BooksApp extends React.Component {
state = {
books: []
};
componentDidMount() {
console.log("MOUNTING");
BooksAPI.getAll().then(books => {
this.setState({ books });
});
}
selectStateUpdate = (book, shelf) => {
this.updateShelf(book, shelf);
};
updateShelf = (book, shelf) => {
BooksAPI.update(book, shelf).then(() => {
let bookscopy = { ...this.state.books };
console.log(bookscopy);
for (let i = 0; this.state.books.length > i; i++) {
if (this.state.books[i].title === book.title) {
bookscopy[i].shelf = shelf;
this.setState({ bookscopy });
} else
this.setState({
books: bookscopy.concat(book)
});
}
});
};
}
Project for reference > here.
you can leverage setState with function and do it like this
updateShelf = (book, shelf) => {
BooksAPI.update(book, shelf)
.then(() => {
this.setState(prevState => {
const updatedBook = prevState.books
.filter(b => b.title === book.title)
.map(_book => ({
..._book,
shelf
}))
return {
books: [
...prevState.books.filter(b => b.title !== book.title),
updatedBook
]
}
})
})
}
What it does ?
Gets the book and remap it with filter && reduce function
Returns new state composed with all books without the one which is update
Returns the new composed state
You try to update state on every loop iteration:
for (let i=0; this.state.books.length > i; i++) {
if (this.state.books[i].title === book.title) {
bookscopy[i].shelf = shelf;
this.setState({bookscopy})
} else
this.setState({
books: bookscopy.concat(book)
})
}
})
Try something like this:
updateShelf = (book, shelf) => {
BooksAPI.update(book, shelf).then(() => {
this.setState(prevState => {
const bookFromState = prevState.books.find(b => b.title === book.title);
if(bookFromState) {
return null
}
return {
books: [...prevState.books, book]
}
})
}

Resources