I'm fetching data from the server to initially populate a form. This form is then edited by the user. Those modifications shouldn't be send be immediately send to the server however, only if the user presses a publish button. I don't want to store serverside draft state either. The modifications still need to be "state" in a way, that multiple components read from it and should be updated immediately as the user types (so I can't just keep the data in the DOM and get it from there on publish).
One way to accomplish that would be to disable refetching in SWR, modify the cache manually with the mutate() or useSWR() hook and a local "fetcher" (which just returns local data instead of fetching from the server). On publish, it does basically the same, only with the "server" fetcher, which sends the current cache to the server.
The other option would be to keep the client modifications in a seperat state manager.
I think both would technically work, so I'm asking more for the recommended way here.
Related
Data usage really matters for my targeted users. So, I am looking for an APPROACH that'll help me reduce the number of data fetching during react rerender cycles in React and React-native App.
Let's say I have a page that renders all the Items fetched from an API. Since the API's data can be updated at anytime, I am obliged to re-call the API whenever the user displays this page.
IS THERE ANY WAY FOR ME TO KNOW THAT THE DATA HAS BEEN UPDATED WITHOUT BEING OBIGED TO RECALL THE API?? Because I think that the less HTTP requests I send the less mobile data I consume (Maybe I am wrong... I don't know)
I thought of implementing a solution with Redux and Socket.io :
I wanted to prepare an event called data-updated that will be managed by socket.io and whenever one of the users performs an action that updates the item lits (The API data), the data-updated event will be triggered and all the connected users will be notified. Then in the data-updated event handler I will update the item list state slice in the redux store.
My worry is that since socke.io keeps the connection between users and the server alive during the whole session, Won't this approach consume even more Mobile data than recalling the server at any rendering??
You can proceed with graphql with mutations and its caching mechanism, its pretty cool and if I mention if it is handled efficiently you can run your application in 2g network also with speed. Checkout its usage and advantages over REST you gonna love it. https://graphql.org/
Another way is by using redisCache but personally I've not used it though you can check.
You can use React-query lib for data fetching, updating, caching etc.you can read more about it but read its docs.
https://tanstack.com/query/v4/?from=reactQueryV3&original=https://react-query-v3.tanstack.com/
Initially the idea of Flux is pretty obvious. The view gets Actions and then emits events based on the Action with which the store is updated. However, when I read through redux-toolkit it seems there are multiple ways of achieving something that should be trivial, namely getting JSON data from an endpoint:
Use the fetch API to get some data from an endpoint
Use RTK query to do the same
Use the React Context API
Using the useEffect() hook
Why are there so many different ways for achieving the simplest task?
Thanks.
This question hides a lot of sub-questions, so I'll try to answer them all as per my experience ( I'm partially a fan of RTK Query ).
To ask for a resource via HTTP from a browser there are not many ways, you have to use xhr or fetch API.
xhr is the old callback-based API, fetch is the modern standard promise-based API.
On top of these APIs many libraries were born to make the process and configuration easier/wider, today you mostly will work directly with fetch or with some gold-standard libraries like axios which is pretty similar to fetch but it has some cool extra features to handle the fetch calls and configuration.
On modern frameworks/libraries like React the problem is not How to retrieve a resource via HTTP, but how to handle the asynchronous call and the side effects that the response creates to the whole application.
So now this opens up to the several options you enlisted.
Basically for very basic fetch calls, you can handle the call inside
a useEffect if it's a GET call that has to retrieve data to show
inside the component that just mounted automatically, or
imperatively start the fetch on a button click for example if it's a
( usually ) POST,PUT, ecc... call.
You can then save the result in a React state, and furthermore pass it throughout your application with a context to let other components access the received response. By the way this leaves you with a lot of other behaviours uncovered, the first one is the loading state during the call, and the error management in case of bad status response or other exceptions. That's surely something you can implement briefly with a useState or better a useReducer hook, by the way, so that's not the real reason why you will feel the need for some async calls manager like RTK Query.
REVALIDATION
What happens if for example you are doing a post call on login of a user that returns the user object with user data, you save that data in a client side state manager ( or React state ) and use that state to display user data in your whole application. During the usage of your application, the user can perform some operations that change his data on DB, so you have to take care everytime he does that, to refetch his data and update his clientside data object, or to return the new user object from each POST/PUT/PATCH etc... call and update the clientside state which is pretty error prone, since you might find yourself having an unsynced clientside/serverside data state if you forget to update the clientside state after each operation, or if there's some issue during these phases. This can often lead to pretty hard to manage and to debug situations.
I'd say this is the First good reason to start using a Fetch manager like RTK Query.
This manager implements a pretty straightforward Tags based revalidation mechanism, that let's you to assign a tag to each Query ( GET ) that you perform, so for example you will make a query when the user logs in that retrieves the user data and you will tag the data with providesTags: 'User'.
Now you can safely perform any number of mutation on your user data on backend, and to each one, clientside, you will tell that as soon as it gets a response, it has to revalidate a certain Tag ( or multiple ones ). For example you might have an editPasswordMutation that changes the user password inside the database, and you will just add a invalidatesTags: 'User', this means that as soon as the user receive a positive from the server confirming that he updated the password, the client knows it has to reexecute the getUserData call, get fresh user data, and save them into the redux global store, so they will be immediately accessible to all the application components that are consuming them.
Totally authomatic.
This whole mechanism would definitely need you to write much more code if you were to implement it yourself.
OPTIMISTIC UPDATES
Most of times you will find yourself not wanting yor UI to wait for server response before showing the effect of a ( for example ) button click. Imagine when you click a thumb up on Facebook, you immediately see it in your UI, and that's not cause they have super fast servers, but because the UI is assuming that the call succedeed as soon as you click the button so it instantly adds a +1 to the likes count of the showed post.
This behaviour is not trivial, since usually when you show data in your application you have and should have a single source of truth. So for example if you are rendering a list of blog posts, you should have retrieved them from your backend database, saved them in your clientside state manager or directly render them as-is. If the first post has, let's assume 10 likes, you will render them based on data.posts[0].likes.count. So that's your source of truth. If you want to add a Like/Dislike button, and you want to implement an optimistic update, you would have to manually alter that source of truth, which is not good at all since it could be shared across multiple components ( in case of a global state ) or to duplicate it, and alter the duplicate, but that's not good either.
It's almost never ( almost ) a good idea to duplicate a source of truth.
RTK Query to the rescue!
With RTK Query Optimistic Update is pretty straightforward to set up, basically EVERY fetch can implement an optimistic update behaviour, since you can use the life-cycle method onQUeryStarted to intercept the immediate start of a call, and then use the utility : updateQueryData to update the data that your mutation is going to invalidate. Extremely SMART.
So in this case you will just have a simple mutation that will add a like to the blog post, that mutation will invalidate the posts data, the single post will be immediately updated in the UI with a +1 like, meanwhile the server will refetch the posts list ( since they were invalidated ) and fresh data in sync will be retrieved, and the user will notice just a pretty smooth experience.
There are many other features, especially involving Cache management, which are a plus, ( even if sometimes they can create some issues ) but I highlight these two main features that alone pushed me to become a user of RTK Query, it might look a bit verbose at start, but it definitely makes things pretty clean and tidy, coherent with the Redux philosophy.
As per docs, the goal of the tool is
It is designed to simplify common cases for loading data in a web application,
eliminating the need to hand-write data fetching & caching logic yourself.
It provides some caching and I personally find it very convenient to have declarative isLoading/error without the need to useState for all of it every time : )
But again, it's only optional: RTK Query is an optional addon included in the Redux Toolkit package.
There are multiple ways to achieve the same goal indeed. There's no gold standard in React world regarding this, for better or worse.
I'm building a real-time "lobby" type web app that hosts multiple users (2-8 at a time), where the state of the lobby is shared among the users. The UI is built with React. Each user establishes a websocket connection to the backend upon joining the lobby. At this time they receive the full global state of the app as a JSON object (its size should not exceed a few kilobytes).
I'm having difficulties conceptualizing the precise state maintenance scheme, and would like to hear your views about it, once I've described the situation in more detail.
The lobby presents to the users a number of finite resource pools, access to which is shared by everyone. The users will move these resources between each other as well as to and from the pools. My current thinking is that the full state of the lobby and all of its resource pools is stored and maintained exclusively in the backend. When a user wants to move a resource e.g. from a pool to themselves or vice versa, or to change the visible state of a resource, this is done with JSON messages sent over their respective websocket connections.
Each action they perform causes a message like this to be sent over the socket (simplified):
{
"action": "MOVE",
"source": "POOL1",
"target": "user_id_here",
...metadata...
}
The users send these messages concurrently at arbitrary times and intervals, and the backend (using a Python asyncio-based server and a data store still to be determined) receives them serially, reconciles each one with the global state in the order they arrived, and then sends the full updated state of the app to every user over their websocket connections, for every single message received. The user who performed the action that triggered the state update additionally gets a status object informing them of a successful transaction, which the UI can then indicate to them.
When a user sends an action message that is impossible to reconcile (e.g. another user has exhausted a resource pool just before their message requesting a resource from that same pool came in), the app still sends them the full up-to-date state of the app, but a status object is included, containing information that the UI uses to inform them that their action could not be performed.
So far, so good. Given the types of actions, types of resource pools, number of users and size of state objects that are to be expected, the frequency of updates should not become a problem, neither in terms of resources nor bandwidth use.
To clarify: none of the actions that the users perform in the React UI mutate their local state in any way. Each and every action they perform is translated into a JSON message like the example above, and the result of that action will be receiving the updated full state of the app, which fully replaces the previous state that React used to render the UI with. The React-level app state is ephemeral, only used for rendering it once. All renders exclusively happen in response to state updates over websockets.
The one area that I'm having difficulties with is how to structure that ephemeral state on the React side so that rendering the updated state object is as quick and efficient as possible. I'm a backend guy and have no prior experience in building a React app of this nature (I last used it four years ago in a really ad-hoc manner, passing props to deeply nested child components, with state stored all over the place). I'm not quite sure what facilities and tools to use.
For example, I could use a top-level context provider with the useReducer hook, touted by many as a "Redux replacement" (which it technically isn't). Or, I could use Redux, but does it actually add any value in this case? Or something else?
Given that the whole state is replaced as a result of every action of every user, what is the best, most efficient, least render time-requiring way of structuring the React side of things?
I would like to suggest that you do not send in the entire state of each and every user over the network instead just send in the modification and let the individual users apps perform the change handling. Once you make this change you could make use.of redux and store the states in a reducer. Also doing this will help you avoid a lot of re-renders as the object references will not change for a lot of your components,
Another thing to add here is that you can store the redux state in the localStorage when the session is terminated
FurtherMore, the one problem that you could have here is that when the user re-connects, he might not get the changes that happened while he was online.
To solve this, you can maintain a transaction id for each user so that the user is sent all the data post that transactionId till the current state by the server and then the app can process and update the transactions
Or the other approach if to completely fetch the data when the user connects for first time or reconnects.
As far as using useReducer or Redux is concerned, you need to decide that based on the complexity of your App.
Cases where the app is small might easily be covered with useReducer and useContext but if you states are complex and you need to maintain multiple reducers, you should go ahead with Redux as it provides moree flexibility with data storage
EDIT:
If the only solution for you is to send the data totally to frontend and let the frontend render it, then you need to divide your frontend code into various simpler modules as much as possible so that no component is using a a complex state.
Once you do that you can make use of shouldComponentUpdate or areEqual parameter to a class component or functional component respectively.
The idea here is to compare the previous and current value that you get from props and let go ahead with the rendering logic or not.
You can store the state as it comes to a reducer inside the redux state, that way, you would be able to implement selectors that are memoized and are able to return data which doesn't change if the actual value hasn't change.
Also while you are using connect for your React app component, its actually a functional component, so unless mapStateToProps returns a value whose reference changes, it will prevent the re-render itself since its a PureComponent
I would strongly suggest, you go through the documentation of shouldComponentUpdate, React.memo and redux. Also look into reselect library that helps you implement memoized selectors
Imagine a React app where you can list your friends in a sidebar. The site uses Redux to store the state of the friends. In case a user adds a new friend to his list, I have to send a POST to the backend, to save it.
The question is, how should I update the list of friends in the sidebar?
After the post, should I make a GET request and add the response to Redux or should I just use the data and add it directly to Redux, removing the extra GET call?
My suggestion will be doing both. When you are making a request to server update the Redux store which will update your state(Friends list) and will rerender the component.
Also fire the GET request action, so that if there are data that are on the server but not in your redux, that should get retrieved.
(imagine: Using two machine at the same time and adding friends)
And if you are using something similar to a pure component, if your redux store and retrieved data are same, i.e., no new data was available on the server, there will be no change in state and component will not re-render. They will re-render only when there is a difference in state and will display the current list.
IMO both options are valid. However, I like to have a single source of truth in our applications, which is the backend in most cases.
Sometimes, you might even choose to go for both options. This will increase the user experience by preventing a loading state, but if the action fails or the backend data is different than your redux store, it can result in "weird" behavior.
My problem is the following.
I have a component that I want to be stateful, and the state based on some ids (per user and per task basically) and this state is saved in my database.
Following some tutorials I managed to do a stateprovider that requests information through ajax requests.
Though I still have a problem since the restore event is only triggered when reloading the page, but not on task change.
Would there be anyways to trigger state change when tasks are opened.
Because the problem I have right now is if I force the restore, it happens but the task will somehow still be opened with the previous state (I guess because of the fact that the provider is asynchronous?)