Get isLoading state of manually initiated RTK Query mutation - reactjs

I'm working on a Nextjs project with RTK Query. Can someone tell me how to access the isLoading state of the mutation that is initiated like this https://redux-toolkit.js.org/rtk-query/api/created-api/endpoints#initiate. I don't want to use the generated hook useLoginMutation but instead get the data by unwrapping the returned result after dispatching like this await dispatch(api.endpoints.login.initiate()).unwrap() because I don't want my form to re render. Thank you in advance!

because I don't want my form to re render.
Hi, RTK Query author here.
Generally, you can use the selectFromResult option to reduce the amount of fields returned from the hook, and as such also reduce the amount of rerenders (as less things are changing).
Generally though, I have to strongly advise you: don't care about the amount of rerenders on this level. React is made to fastly rerender your components. One, two, or even five rerenders within a 1-3 seconds frame should not make any difference for your application - usually, your component will rerender on every keypress in your form anyways!
If, and only if you are seeing actual performance degradation, you get to the point of optimizing things like this - and then you would see if somewhere you have dozens or hundreds of rerenders, but still do not care about single-digit rerender numbers. If single-digit rerenders are a problem for you, you have heavy computation logic inside your components, and you need to optimize that - the execution of one of your component functions should always be in the sub-milliseconds. Then that's the place to optimize.
As for triggering the mutation with initiate: It will be loading until your await continues and done after that. If you just need that information, there is no need to access an isLoading state. You must set the component state if you need that information in your component. And that would trigger a rerender. You see where I am going: use the hooks! That's what they are made for.
Also, if using initiate: make sure to unsubscribe the result afterward or it will stay in cache forever. Again, this is something the hooks do for you.

Related

React usage with RTK Query can cause memory leak on unmounted component

When using RTK Query within a React component, it's very possible for a memory leak / no-op error to be thrown if the component is unmounted before the query completes.
For example, if you use isError or isSuccess RTK will attempt to set these even if the component is unmounted.
Previously I would manually check that the fetch was not aborted and only set state if needed. I'm unsure how to handle this with RTK Query where the state is handled automatically.
Perhaps I'm misunderstanding how to deal with it, or RTK Query is able to handle this in some way that I'm unaware of.
This error message can be ignored at this point, because it's actually already going away in React 18:
https://github.com/reactwg/react-18/discussions/82
To sum up, we're removing this warning. The case for which it's relevant (subscriptions) is not very common in the product code, and is typically encapsulated in custom Hooks and libraries. The case that most product developers encounter is the one where this warning is not only useless/misleading, but also pushes developers to worse solutions while trying to suppress the warning. Hopefully, this change will reduce the confusion, and let you remove a bunch of isMounted checks from your code.
Is this your interpretation or are you actually getting an error message here? In RTK-Query, that data will be persisted to the store (in case you navigate back) but will be removed after 60 seconds if no component using it mounts. A memory leak is pretty much impossible, it's just not bound to the individual component because it builds on Redux.

useContext with immutableJS

I have an Object of data that I store as my context. It gets filled with API responses and I feel like performance-wise I'm doing something wrong.
I tried to look into ImmutableJS but there is no reference of combining ImmutableJS with ContextAPI. Is combining them together doesn't give any benefits? And if it does do have an example of to how develop such a thing?
Whether you use context directly, add redux or pass down props through your component makes no difference to how you use immutable. ImmutableJS simply makes sure that an object does not change unintentionally and that it can be checked for changes with fast reference equality.
The problem you describe could arise from the fact that your context object changes whenever any response data is modified. Therefore it would trigger a rerender on all context consumers (see caveats). I like Immutable and use it in a large project, but if I am correct, it won't make a difference, you would have to fix the root cause or handle changes with shouldComponentUpdate.
I suggest you look into redux (or some other context provider) or show us code.
Don’t try solving your problem by introducing a dependency. It won’t be a magic bullet.
From what you describe, your context object changes frequently and is causing your component tree to rerender too often.
Maybe try creating multiple contexts that are only relevant for parts of the app. That way rerenders will only happen with subtrees using specific context.

Redux Selector or Effect

Please help me to make the decision, regarding which technique to use.
I have a big list (up to million rows) that is kept in redux state.
Once user types in filter criterion, I want to apply filter and show filtered data (actually piece of it with react virtualized).
I do understand that ideally this is a use case for simple selector (or memoized reselect).
The problem that I see: filtering itself may take 2-3 seconds, thus, I have to use debounce. For debounce i have to use middleware, because debouncing is asynchronous and impure.
Can effect (epic) take data from state and act as asynchronous selector? Or maybe there are some patterns for implementing debounce in selector?
Appreciate any advices.
I have a big list (up to million rows) that is kept in redux state.
Really? You're doing blunder mistake. This will consume huge memory. And some browser may not respond them correctly.
So, I would suggest you to apply filtering from database results. On each filter being applied, the application will need to get the results from the database say upto 10, 20, 50, or 100.
With this sense, you can keep using redux or use memoized effect depending on your application logic/complexity. Both will work fine without worrying about debounce method.

What's the point in fetching remote data in componentDidMount method if it will cause change in what is shown to user?

You are right that there are too much words. The only remaining question now is:
Why is it considered to be normal to fetch data in cDM method if it is called after rendering and will cause an extra re-rendering and re-drawing after data has been fetched? More to it - user will see screen flickering.
I've spent two hours reading SO and other articles and still have questions. I'd like to get a better grasp of things and reassure them that my understanding is correct.
It all started with learning hooks and thinking about the difference between useEffect and useLayoutEffect. I learnt that useEffect should be used when things which it does don't affect what is shown to user (some background job) and useLayoutEffect is fired "synchronously before painting" - so, for example, should be used when I want to show a list on screen and data for it is fetched form somewhere else. Thus, if I fetch data in useLayoutEffect it will fetch the data first, and only after that update state and show the screen with that data to the user. If I used useEffect it would show to the user an empty list first (because it is asynchronous and doesn't stop painting) and then re-paint it after data is received. Confusing part is that it says that useLayoutEffect, the same as useEffect, is called after the rendering has finished. I explain why it doesn't make any sense to me in the last paragraph.
Another confusing thing is that it says that useEffect should be used in the same place where cDM or cDU methods are called. But I've been fetching data in cDM method since I started using react.
Does it mean that I did it wrong all the time? Because I should have done it where useLayoutEffect is fired. And this brings me to the next paragraph.
The most confusing thing - a relationship between rendering, painting and cDM method. It says that cDM is called after the rendering has finished. It also says that "rendering has finished" means that DOM, layout, sizes, styles - everything is calculated and ready to be painted by the browser. I understand it in a way that at this point it makes no sense to do anything that would change what is shown to a user - because it is all already calculated! Shouldn't there be a hook that is called before rendering? For example, I call this hook to fetch data for some list which is shown on screen. Only after data is fetched rendering should occur because now react knows how many elements are on the list and how much space will it take and so on. I mean what's a point in making rendering before you know all your data? Isn't react forced to recalculate everything after data is fetched?
I really hope you will help me to understand these things. Thank you.

React performance in mobile browser

I have a component (table) that contains many lines with data editing in them, with masks in the form of contenteditable, it is possible to select all fields and change all the lines at the same time.
On desktop it works pretty fast, but on iPhone 6 I have unreal lagging, with Safari hanging for 20 seconds for each action.
I completed the recommendations to improve performance from React: prevent reconciliation, pure components, etc ...
Are there ways to improve performance? Is it necessary for me to ponder a functionality change on a mobile device, in favor of performance?
You should override shouldComponentUpdate component life cycle method for every row of the table. Ideally for every cell in every row.
If you have a table and it gets a different props. What happens is that every nested component gets re-rendered. PureComponents might help but writing shouldComponentUpdate is the only way to really control when something gets re-rendered.
Also for really large data list there is a react-virtualized. Check it out. Its pretty cool.
It would be nice if you could post source code though.
Adding to above answer, you should use react-perf module to exactly validate if any change actually made a performance gain.
https://github.com/crysislinux/chrome-react-perf
use this extension to exactly see how many times, each component on your page actually rendered, when you did your problematic/slow user interaction
try reducing no. of renders of each component. Also reduce the no. of components rendering on each such interaction.
try minimising time taken by each component. You can sort by time and focus on the most expensive components. Avoid rendering components higher in heirarchy, first. To find the exact reason behind a component's rendering use following method.
Put componentWillUpdate lifecycle hook, temporarily, and diff previous and next props/states. With this you would get the exact prop culprit behind a rendering. Unneccessary prop change might be in following scenarios:
When prop is just a function which is changing because of 'bind' usage or arrow-function usage, which changes the function reference everytime, and with that, causing new renders, everytime.
There might be a prop being initialised with new Object() or {} notation, which is considered new object everytime and hence new rendering. This can be avoided by a const READ_ONLY_OBJECT = {} and using READ_ONLY_OBJECT everytime a variable needs initialization.
You might be unnecessarily mutating object-type props and doing diffs in componentWillRecieveProps.
There can be more reasons to where we dont want a render but it happens because of the ways react works. Just see that props dont change unnecessarily. Also, dont put unnecssary componentWillRecieveProps/shouldCompoentUpdate checks as it can impact performance negatively. Also when using these, use these as higher in heirarchy as possible.
Some techniques to use
try to avoid using react lifecycle hooks which run on each render.
try reducing any scripts runing on every render.
use componentWillReieveProps, but only if you gain, else point 1 can reduce gains also. Also, using this often can lead to non-maintainable code. Always validate the gains with react-perf tools, before making changes related to optimizations.
use throttling in chrome-dev-tools to create slow device enviroments, use javascript profiling to see which javascript methods took most time.
Try using redux with react, to manage state. Redux also has componentWillReieveProps thing implemented for connected components. So, using redux will help.
When using redux use an appropriate batching stategy. You can also use batch middleware in redux.
Also, similarly, in react try to do events in batched manner so as to reduce amount of time spent in react's renderings and diffing algorithms. Try clubing setStates in react or actions in redux to reduce react scripting time.
Always use appropriate throttling/debouncing techniques while implementing input controls, to get immediate response. You can also use uncontrolled components to have immediate response, if your business logic allows. Idea is to not to run any javascript when user is typing or interacting with your page in any way, else he would notice the jank in devices particularly bad in computing power.
Your action handlers should not be lengthy. If lengthy, try to do them in chunks, asynchronously, with redux-actions or with just promises.
There is more to this list, but the problem is, react as a framewaork is pretty easy to get to work, initially, but sooner or later, any medium sized react app will run into these performance problems, as react's diffing and rendering logic incurs a lot of performance penalties in bad devices, as soon as app grows in size, only option is to get react-performance tools, also, into the build process, asap. This will let you validate and measure your improvements.

Resources