How and when to persist user interactions (upvotes / likes) using React & Redux? - reactjs

Trying to understand how to best implement this:
I have a CRUD app built in React / Redux / Postgres and I want the ability to persist something like user upvotes on an article. With Redux, I can simply fire an action that toggles it on/off, but if I want to keep track of how many upvotes it has, I have to persist each upvote to my postgres database.
Here's what I'm thinking so far, but not sure if this is the correct way to handle something like this:
On the click of the upvote, an action should fire increasing the vote value in redux. The POST request to my server would then be made via an action creator called within componentWillReceiveProps() as the store updates and passes in updated props.
Is this correct?
Thanks, any and all help is greatly appreciated!

It is probably a better idea to keep your store in sync with your db, and use the local state of the component to store the temporary upvote until the backend replies back. Thus you can do somehow like this:
Trigger an action and set local state of the component to +1 vote
The action sends a POST request, and on the successful reply sends an update to the redux store
The component receives props with the new value (that reflects the actual update of the db) and flushes its state in the componentWillReceiveProps hook. In case there can be other updates coming in to the component, you can avoid flushing state by checking if props did actually change.
This is a somehow a naive example, since in the real world application you would probably want to add error handling and communicate failed requests to the user

Related

Maintaining the full state of a React app in the backend?

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

Fetch update from backend or update redux

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.

React + Redux App: Calling API through a Redux action Vs Calling an API directly

I'm working on a React+Redux application. I'm calling APIs through Redux actions and storing the response data in the Redux state. But there is a case, Where I don't have to store the API response data in the Redux store.
So the question is, Is there any valid reason to call the APIs through
Redux actions or Should I call the APIs directly since I'm not storing
the response data in Redux store?
It depends on what kind of call you're trying to make, and who's concern it is.
Here are a few cases:
Is this a one-way call to track something?. You can fire an action that gets picked up in a middleware. this is a good case for sending analytics.
This doesn't have to be stored in Redux's store.
Is this a call where some other part of your application will need this data?, then this is a good use case for making an update in the Redux Store so other components when read this and use props to decide what to render etc.
Is this a call where it only concerns one component or isolated part?. You can make this call inside the component in componentDidMount since this doesn't concern anyone else
Alternatively take a look at Sagas, they observe all actions that get dispatched and decide what to do with them in a clean way.
The accepted answer quite well explains the scenario where from API call can be initiated. For better user experience, we always show some spinner or busy sign to inform the user that a request is being made and it has not finished yet. It may happen that API response is not mutating the state, but to let the user know some task is going in the background, we usually update store (for global access) or state (for component level access) with value like isFetching or anything meaningful.
So, it depends on the developer, whether he/she want to show some busy sign or silently perform the API request. Moreover, if they want to show busy sign then, they should decide which part of the application should be aware of the API call. If it is restricted to the component level only, then no need to make the call in actions, otherwise, for global level, yes it should be inside action.
For the sake of uniformity, you should always follow the redux way, even though all the responses are not stored in Redux. It is also a question if you are not using the response from an API call why are you making the call. This argument is counter-intuitive. If you are using the response in some way better do it the Redux way. It is advised to always store the response to a call to Redux and use it, I am sure you are making API calls to do some action on UI.

Appropriate place to dispatch action for fetching server data

I'm new to React and I'm trying to figure out the best way to request information from the server based on the URL. I'm using Redux and React Router v4.
Let's say I have a route /foo/:id, and a component Foo that will render something based on id. However Foo needs some server data related to id to do so. I believe the way to accomplish this would be to use mapDispatchToProps to create a function that takes id as input, does some async work, dispatches an action, and ultimately updates the redux state.
My question is: where is the most appropriate place to invoke the dispatch? In this scenario, there's no form submission or button click to kick things off. Originally I was thinking of including a check for the id data in render() and fetching if it was not populated, but this felt wrong due to the side effects.
You can do it in componentDidMount of the Foo component, similar to this example from the Redux GitHub project.
Your intuition is right that render is not a good place to do so. Most people do it in the componentDidMount lifecycle method of the component.
On a relevant note, you will also want to do fetching also in the componentWillReceiveProps method like what they did here. Reason being if your user navigated from foo/1/ to foo/2/, the component is already on the screen and will not be mounted again, hence componentDidMount will not be called again. The fetching for the second user will be done in the componentWillReceiveProps method.
i think the best way to do the dispatch inside the componentWillReceiveProps() which would help you fetch some data before the component renders
It seems your use case is well-captured by the react-refetch package which you can find here. It provides a higher-order component that allows you to specify dependencies at specific API endpoints and then resolves them when a new instance of your component is created.
Importantly it injects the data into your components props using a synchronous abstraction of a promise called a PromiseState. This will allow you to conditionally render your component depending on whether the data is say pending, fulfilled, rejected, etc.
This is not attached in any way to Redux, it skips that layer entirely, so do keep it in mind that the response is directly injected into the component and does not go through your redux store's state.

react-router and flux - clearing state while transition

I am using react-router with the flux architecture (facebook's flux implementation).
Currently in my system I have route that says "chat/:topic".
When the user is entering this component, I am creating a subscription (using action creation, on componentWillMount) to a websocket server, and I am removing the subscription on componentWillUnmount.
When the user is moving to another route the whole workflow works alright - because react-router is unmounting my component.
When I transition inside my route (from "chat/games" to "chat/tv"), the component isn't mounted and I need to clear my state of the components.
I read about different actions that I can take and this on transition to dispatch an action "TRANSITION" and every relevant store will clear it's store.
In my opinion, this kind of action - is wrong, it couples my stores and my router.
How would you solve this problem? Is this an issue that I should raise to react-router and ask them to unmount inside my route?
I found the answer thanks to gaearon (https://github.com/gaearon/),
Use a store to keep the selected topic and ask the messages store for messages, in flux you shouldn't remove anything from your store, unless you need it for a performance reasons.
In my application, I must remove the messages (since they are large objects) and clear my subscriptions (to reduce the load on the server).
In order to achieve this there were three solutions that I found:
Use componentWillReceiveProps and check if the params are changed, if the params are changed - do whatever you need in order to clear the store - for example call ActionCreator and reset the state.
Send a dispose payload in transition (inside Router.run) which will tell all the stores to clear themselves.
last solution (which I used) making sure that my components are unmounted - Why? It is too error prone to clear the state on componentWillReceiveProps/dispose and it is clearer to just ensure the components are unmounted.
Details on how to achieve this:
https://github.com/rackt/react-router/issues/292
https://github.com/rackt/react-router/issues/496#issuecomment-64152941
I believe that compomentWillReceiveProps might solve your issue. This is what react router uses to pass stuff to you.
As far as I know, you need to use both componentWillReceiveProps AND componentDid/WillMount to catch the initial render also.
I'm anxiously awaiting the react-router 1.0 release (this weekend?) in hopes that there is a more elegant way to do this.

Resources