Dispatching clear state action in redux - reactjs

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

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

Handling loading state in a React app that uses Redux with redux-thunk, going back to component after data has been fetched once

I have a React app that uses Redux with redux-thunk. Given a certain component that needs to fetch information from an API, I call a bound action creator to make that request after it mounts:
componentDidMount() {
this.props.fetchSomething();
}
For the UI to know whether it is in a loading state, I use a loading variable from the application state. My reducer handles three action types for LOADING, SUCCESS, and FAILURE. The loading variable is set to true when the LOADING action is emitted; then it is set to false on SUCCESS and FAILURE.
Thus there is the following in the render method:
render() {
if (this.props.loading) {
return <Spinner />;
}
return (
<div>
This part uses the fetched data, available via this.props.something
</div>
);
}
Because initially this.props.something is null, I also have to check for its existence before rendering the desired template, so the above if-statement becomes:
if (!this.props.something || this.props.loading) {
return <Spinner />;
}
This approach has served me well so far, but there are some issues, one of them being:
The first time I access the page, this.props.something is not yet set. But the second time I access the page, with the app already loaded, that data had already been fetched once, so this.props.something will have been defined. Because of that, there is brief moment in which the if statement condition is FALSE and the actual component template gets rendered.
(1) How would you guys take care of that issue?
(2) And with your approach, how would you handle a component that had to make multiple requests, like five of them, using the same approach above, with a different loading/something variable for each? I can duplicate the same approach above, but get the issue of brief if-statement failure for each resource that is already defined, but not loading.
1) You have two options. U can dispatch some RESET action on componentWillUnmount and reset your store, this.props.something will be null again. Second option is to show data if u already have them, show spinner and when second fetch is success merge it together. (depends on purpose and UI)
2) I have never needed it but what about to store it in redux as map {componentName: loaded} and check if all components are loaded?
Edit: When u set initial value of loading to true you don't need to check something prop.

How to deal with query params in react + react-router + flux

I'm trying to replace a Backbone.Marionette App to React and am facing difficulty thinking about query params. I think I'm missing a really simple peace in understanding this pattern so I apologize if this question is totally nonsense. I would appreciate any support or just pointing me to some direction that I can google more specifically.
There's a /users page which lists users and you can filter the users via search bar. So if you want to filter the users which contain 'joe' in their username, I would make a request to the server with query params like /users?username=joe. In addition I am able to paginate by adding a page parameter, too (/users?username=joe&page=1).
If I only think about the functionality, the flow would probably be
The Client inserts joe to the input element and clicks Search.
Clicking the Search button fires an Action (like Action.getUser).
The Action makes a request to the server and receives the results
The Dispatcher dispatches a function with the results payload to whomever (usually the Store) is interested in the Action.
The Store's state changes with the new result received by the Action
The View (Component) re-renders by listening to the Store's change.
and it works as expected. However, I would like the Client to be able to bookmark the current filtered result and be able to come back to the same page some time later. This means I will need somewhere to save explicit information about the search term the Client made, which is usually the url (am I right?). So I will need to update the url with query parameters to save the search term (/users?username=joe&page=1).
What I'm confused is where and when to update the url? What I can come up with right now are the 2 options below - and they don't seem to be clean at all.
Option 1
The Client inserts joe to the input element and clicks Search.
Clicking the Search button fires a transition of the ReactRouter with the new query params (/users?username=joe&page=1).
The View (Component) receives the new params via this.props.params and this.props.query.
The View (Component) fires an Action like Action.getUser depending on the query params it receives - in this case username=joe&page=1.
after this, it is the same as above
Option 2 (only 6 is different from what I explained above)
The Client inserts joe to the input element and clicks Search.
Clicking the Search button fires an Action (like Action.getUser).
The Action makes a request to the server and receives the results
The Dispatcher dispatches a function with the results payload to whomever (usually the Store) is interested in the Action.
The Store's state changes with the new result received by the Action
The View (Component) re-renders by listening to the Store's change. And somehow (I don't know how, yet) updates its url depending on its props (like this.props.searchusername, and this.props.searchpage)
What is the best practice on handling query params? (or this may not be specific to query params)
Am I completely misunderstanding the design pattern or architecture? Thanks in advance for any support.
Some articles I've read
Any way to get current params or current query from router (outside of component)?
Async data and Flux stores
Make it easier to add query parameters
React Router and Arbitrary Query Params: Page Refreshes Unintentionally on Load?
Add default params?
I would consider best practice to be the submit button only setting the location query (username). The rest should be taken care by the main react component that is assigned as router component. By this, you can be sure that anytime one revisits or shares the url, they can get the same results. And this is very generic too.
Something like this:
let myQuery = this.props.location.query;
if (myQuery.username) {
let theUser = myQuery.username;
this.setState({
userName = myQuery.username
});
} else {
this.setState({
userName = false //Show All
});
}
And then use this state "userName" to send to the server to search with. By this way, you will not need to iterate the code of the component that takes care of listing users since server already sends the relevant data.
In my experience with using location queries in React, I have been very content with their reactivity cycles and performance. I'd highly recommend keeping every different app state in relevance with the url.
Not entirely sure what you mean by
this means I will need to update the url to save the information (/users?username=joe&page=1).
You will probably have a similar structure to this.
TopContainer.jsx
-- Users.jsx
-- a list of User.jsx
Usually TopContainer will watch all the stores and if anything changed, pass it down to users.jsx. That way in Users.jsx, you can simply render this.props.users without worrying about any reRendering.
The search users actions usually happens in TopContainer's componentWillMount event, and you the page will listen to UserStore. That's a good place to throw in any query params. Something like this would work
componentWillUnmount() {
let searchTerm = router.getCurrentQuery().searchTerm;
UserActions.searchUsers(searchTerm)
},
The page doesn't really care if the url has a query params or not, it just dumbly shows whatever in the user store.
Then when the search finishes, Users.jsx will be reloaded and show the correct results

How to update a child component state after an ajax call

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

Resources