How to update a child component state after an ajax call - reactjs

To learn react + flux, I am building a simple compose box where the paragraph is broken down into multiple tweets and then by clicking a button you can tweet the series of tweets. The high level app has the following components:
<div>
<ComposeBox tweetParaText={this.state.tweetParaText} ref='composeBox' />
<DisplayTweets tweetCollection={this.state.tweetCollection} ref='displayTweets' />
<TweetButton signedInSignature={this.state.signedInSignature} ref='tweetButton' />
</div>
Once you are done composing the text, you click on the TweetButton. The TweetButton has 4 UI states:
Ready (Show the Tweet Button)
Tweeting (in which case I show a
loading gif as well as change the text from "Tweet" to "Tweeting..")
Tweeted Succesfully (In which case I want to revert text of the button to "Tweet", hide the ajax laoder gif and show a message "Tweets sent successfully!" for a duration of 7 seconds.)
Error (In which case I want to revert text of the button to "Tweet", hide the ajax loader gif and show an error message)
These states are unique to the tweet button and are different from the application state which I am storing in the store.
The onclick of the TweetButton component is as follows:
_onClick:function(){
this.setState({buttonState:tweeting});
ActionCreator.tweet(this.props.tweetCollection,this.props.signedInSignature);
}
On changing the state here, the UI changes for this state happen, as that is how the component has been written. However, I am a bit stumped after this.
The flow of data being unidirectional, the action upon completion dispatches an event to the store which updates its state and thus the state of the ComposeBox component. But how and where do I set the state of the TweetButton component? Should I write a different store for this? Or is there a better way of achieving this?

I use the same store for the status/state of the call.
Lets say the result of your call will be stored in _Tweets = {}; inside the TweetStore object.
I register the results uniquely (not mandatory I guess) inside the store like this
_Tweets["someresultid/tag"] = {
data: result.data,
status: result.status
}
TweetStore is listening for 2 events : case : "Loading" and case : "ReceiveResults/answer/wtv"
When the user initiate the call you'll dispatch "loading" at the same time than your call. Then when it receive the answer you dispatch "answer".
Your view is listening for any change of your store, when a change occurs the callback will check the status and rerender accordingly (lording,error or result).
I hope it helps

Related

How to avoid letting an async thunk that is no longer useful (the app is not expecting it anymore) from being able to update the state?

I have the following pattern on my single page app (React + Redux).
It runs every time a load a page on the app. User navigates to a specific page, and the loadPageThunk is dispatched. The initial state of the page shows a spinner to the user. This is used for example, in a blogpost page.
That thunk will get some async data (the blogpost), and then will show the page with that data.
It works fine. When the user navigates away from the page. A useEffect dispatches a RESET action to reset the state back to its initial value.
My question is:
What if the async call takes too long to complete and the user navigates away? It will create a problem because now there's a pending promise that will complete in an unexpected time. How can I prevent that completion from updating my state?
Imagine the following steps for an async call that is taking 10 seconds to complete:
#### FIRST PAGE LOAD ####
USER VISITS blog/slug-1
loadPageThunk() IS DISPATCHED
blogPost1 STARTS GETTING FETCHED (WILL TAKE 10 SECONDS)
USER NAVIGATES AWAY
#### SECOND PAGE LOAD ####
USER VISITS blog/slug-2
blogPost2 STARTS GETTING FETCHED (WILL TAKE 10 SECONDS)
USER IS STILL SEEING SPINNER
blogPost1 (FROM THE PREVIOUS VISIT) HAS COMPLETE AND WILL UPDATE THE STATE
USER NOW SEES blog/slug-2 WITH THE DATA FROM blogPost1 WHICH IS AN ERROR
blogPost2 WILL EVENTUALLY COMPLETE AND USER WILL SEE A CONTENT FLICKER ON THE PAGE
QUESTION
How can I avoid pending promises that are no longer useful from being able to update the state?
This problem is not currently happening in my app, but I think that a good design should account for that.
Should I add an ID for my LOAD_PAGE cycle, so I can check the ID of the current cycle before allowing callbacks / async code from updating the state when IDs don't match? How do people usually handle this?
Personally I store blog data as entities (posts, comments, etc.) keyed by id and collections. The collection is just the array of post ids on a particular page.
For example,
{
entities: {
posts: {
1: {...},
2: {...}
},
comments: {
123: {...},
999: {...}
}
},
collections: {
"blog/slug-1": [99,98,97...],
"blog/slug-2": [89,88,87...],
}
}
This sort of structure means that every page can save its data in the correct place regardless of whether it is the current page or not. It also means that every page can select its own data and can see whether that data already exists in the state.
The promise returned by createAsyncThunk has an abort() method attached which can be used to 'cancel' the promise. See canceling while running. You can call abort() in your cleanup to prevent the thunk from being fulfilled.
In your reducers, if you are handling the rejected case for your thunk, then you can add an exception for cases where the error name is AbortError to do nothing instead.
To expand a bit about your specific situation: a good rule of thumb is that if you find yourself 'resetting' state when you unmount the component, then it should have just been local component state in the first place.

Is it normal in React to use `useState` to manage whether a form has been submitted?

A basic online React tutorial is demonstrating how to create a React-based form with 3 fields (first name, last name, and email). Surprisingly to me, it has useState to manage whether or not a form has been submitted, which it uses to decide on whether to show a success message or not.
Please note that it does NOT make an actual form submission (i.e. no API calls) and so I am wondering whether it is using state only for simulation purposes here.
Here is a snippet:
const [submitted, setSubmitted] = useState(false);
...
{submitted && !values.lastName && <span id='last-name-error'>Please enter a last name</span>}
...
{showSuccess && <div class='success-message'>Success! Thank you for registering</div>}
...
Is it normal for real-world React applications to useState to manage whether or not a simple form such as 'contact us' or 'feedback', etc, has been submitted? My guess is that normally there would be no need to do so.
As the resulting API call, whether 'success' or 'fail' could simply be used to show the state of the error message. Thereafter, the form should ideally reset itself to allow for another submission. So, there would be no need to store the state of the submitted form. Am I correct in my understanding?
Your question is a little confusing. basically if you want to store a data in React and this data has direct effect on your application you mostly should save it in state. When you submit a form, the form onSubmit event handler will be called and you can do everything in that event handler. It's clear that in the tutorial submitted state is a flag to simulate the fetch process. Usually when you want to handle submitting a form and fetching API you should store 2 items in state:
Error and Loading
You should use loading flag to show a loading indicator during fetching API and use error to check if any error exist store and show it. If API fetches successfully you may redirect user to another page, show a notification or change some data in your state. It's up to you. But be sure the submitted state in your tutorial is just an example ans simulation. But it has real usages in real world! Hope it helps!
Here is an example: I want to add a user by fetching an api and i want if api fetches successfully add it to the list:
Sandbox

Implementing a local like/unlike feature in NextJS and making it persist between client-side routes

I have a blog front-end app built using NextJS and it looks like this:
Each card here is a functional component called PostPreview.jsx. As you can see, each component comes with a heart icon. By default, this icon is to stay gray. However, when clicked it turns red, signifying that the post has been liked. This action only occurs if the user is logged in. If not, clicking the heart icon presents a login modal.
Right now, I'm only focussing on making the "like" persist between client-side navigations, i.e. without any interaction with the db/server.
As of now, clicking the icon, toggles the color alright. However, it fails to persist when you navigate away, say, by clicking on a post title and then hitting the back button to return to this page. What is the recommended way to achieve this functionality?
The entire codebase can be found at my repo here: https://github.com/amitschandillia/proost/tree/master/web
The code for the component in question (PostPreview.jsx) is at: https://github.com/amitschandillia/proost/blob/master/web/components/blog/PostPreview.jsx
The site is live at https://www.schandillia.com/blog.
I understand I could use Redux, but not sure how to prevent the value from being reset upon each re-render even when using Redux.
Illustrating the problem better
Visit blog page; several instances of component (PostPreview) render for the first time:
Receive array of post "likers" via the likedBy prop object.
Retrieve logged-in user's ID from the Redux store via userInfo.userID.
Look up userInfo.userID against the likedBy.readers array of IDs.
If user ID exists in readers array, post is liked, set liked to error to turn the heart icon red and push post's id to the likedPosts redux store.
If user ID doesn't exist in readers array, leave liked to inherit to leave it gray and remove post's id from the likedPostsredux store.
Like a post; click the heart icon:
Set liked to error to turn the heart icon red.
Push post's id to the likedPostsredux store.
Unlike a post; click the heart icon:
Set liked to inherit to turn the heart icon red.
Remove post's id from the likedPostsredux store.
Now click any link on page to navigate away from the page (client-side routing, no server contact here). Then hit the browser's back button to return to the blog page.
At this point, the component (PostPreview) re-renders and the redux store will be reset in accordance with the original likedBy prop object. This, of course, means that all the changes since the first render are gone. This is where I need help. How would you handle such a situation where user interactions like likes and dislikes have to be persisted across client-side navigations and also honored across re-renders?
I see two ways:
1) Simple: By using local storage you can write array of likes
likes: [likedPostId1, likedPostId2, ...]
And then in PostPreview check if current card id included in likes array
let isLiked = likes.includes(currentPostId);
2) Right: By using Redux, it's the same way, but you'll store likes array in Redux and use for page navigation react-router.
It's essential to understand how navigation works in SPA
For simple SPA implementation:
Simple solution would be to just use Context API with hooks (refresh will reset it)
1a. Another simple solution is to use LocalStorage with hooks (refresh or back button will persist it)
Than you could refactor it into REDUX (a lot of boilerplate)
2a. so maybe you could just use GraphQL as an app state
Here is an article explaining how to do that highlighting Redux vs hooks with Context API:
https://www.sitepoint.com/replace-redux-react-hooks-context-api/
Also it's worth to note that REDUX or GraphQL will not persist it itself between refreshes
4. If user ID exists in readers array, post is liked, set liked to error to turn the heart icon red and push post's id to the likedPosts redux store.
5. If user ID doesn't exist in readers array, leave liked to inherit to leave it gray and remove post's id from the likedPosts redux store.
You should wrap #4. and #5. as one action in redux store.
const mapDispatchToProps = (dispatch) => {
return {
onUpdateLikedPosts: (data) => dispatch(actions.updateLikedPostsAction(data))
};
};
And, Execture the function at when component of PostPreview is mounted.
const componentDidMount() {
this.props.onUpdateLikedPosts(data)
}

React -unbale to load the contents after fetch api call in child component

I have mainly 3 components-
Contact Component - it will load ,since the fetch api is written in ComponentDidMount and we can see the data-its showing the list of users
And when you click on particular user-it have id as param and will call another api in handleClick and will get the response successfully and updated the state. Ideally when state updated,contact Component render need to render again,and we have the data in state and it will show the child component,but its not showing.
My understanding is that whenever state changes component will re render again ,at that time,my condition gets satisfies and will show the child component.
Please correct me if am wrong
here is the link to editor with code
https://codesandbox.io/s/pensive-microservice-4jem7?fontsize=14
```Step1-Click on Contact link```
```Step2-ComponentDidMount works and will load content```
```Step3-try to click any edit button of any user-HandleClick will work and will fecth data and update state,but its not showing the child component,thats is supposed to render when data is there```
There is a logical error
The fetchUser is an object so the conditional rendering fetchUser.length > 0 && <Component /> will not do any component render as (object.length = undefined) > 0 = false.
Due to the above your child component will not have an array to map() so just show the object there.
The Fixed Code can be seen here on CodeSandBox.

Dispatching clear state action in redux

I'm using redux in my react project. My question is how to clear state when some works that use state finished.
For example, I have search state in my redux store.
search:{
status: 'INIT',
result: [],
}
Since my search is an async request, it has status field.
when the user searches a word, it will dispatch 'FETCH_SEARCH' to make status to 'LOADING'.
when fetch done, it will dispatch 'SUCCESS_SEARCH' or 'FAILURE_SEARCH' to make status 'SUCCESS' or 'FAILURE'.
And in my component that shows the result of a search, it renders depend on search state's status. If a status is 'SUCCESS', it will render the result of the search.
So far, there is no problem.
but what if the user has searched before and try to search again after some works? Since the user has successfully searched before, search state has 'SUCCESS' state. So my component will render 'unwanted' result.
So currently, i dispatch 'CLEAR_SEARCH' action to initialize search state.
My question is how to clear redux state after some submission has done.
Am i doing correctly? Or is it anti-pattern?
I've done this before and what I do is follow a workflow.
After a search submission you shouldn't clear state. You should reset it when the user searches again.
Let's say the user searches "Term A". You "SEARCH_SUCCESS" is set and it's rendering the results. When he searches again put that state to "SEARCHING" or "LOADING" as you have it.
Now in your render method if you have something like
// etc..
if (isLoading) {
return <LoadingComponent />
}
// etc..
You will render a loading before rendering any results. Then your search action should set either the SUCCESS or FAILURE and if you have your components mapped out correctly it should be really straightforward.
EDIT
After a brief discussion in the comment section you wanted INIT only at the start (the first ever time it's rendered) but then resetting the state still.
So you can go for this workflow:
First time:
-> INIT -> SEARCH_TRIGGERED -> LOADING -> SUCCESS or FAILURE
Second time (still in same screen):
-> SEARCH_TRIGGERED -> LOADING -> SUCCESS or FAILURE
And then you would have the desired workflow.
Hope I helped

Resources