React and React Native pattern for handling server responses - reactjs

In the past I did lots of native mobile development on both Android (Java) and iOS (ObjC and Swift). For my server-connected apps, one issue that I had to handle properly was the callback that was called after the app made an HTTP request. For example, suppose I have a screen (an Activity in Android or a ViewController in iOS), and inside that I make an HTTP request to a REST server, expecting some data in response. Most textbooks (unfortunately) provide examples of the HTTP response calling a callback method within the Activity or ViewController itself. Yes it seems reasonable at first glance but there is a big problem in apps with multiple Activities or ViewControllers - what happens if the user navigates away from the screen after the request but before the response comes back? In this case, the app destroys the memory of the first Activity or ViewController, so when the response comes back, it tries to call a method on freed memory, resulting in a crash.
What we had to instead was to use a persistent singleton object (like a class that extends Application in Android or use the appdelegate in iOS) as the class that implemented the callback functions. Any screen that wanted to get results had to register for those results (using listeners in Android or using notifications in iOS). If the user navigated away from the screen, the screen's appropriate lifecycle methods would unregister the listener/notification handler in the screen so that there would never be a case where a callback method is called on freed memory.
Now I am starting to use both React (in browsers) and React Native (in mobile apps). I am using the Axios module for handling HTTP requests. Once again, all the examples I see show callbacks in the React component itself rather than any kind of pattern where there is some sort of persistent singleton or global object that handles the responses and dispatches them to any screens that are still active on the display.
So I have several questions as follows:
For React (in a browser), is this a concern? Can/should I just provide a callback on the component itself, and the browser will not have a problem if I navigate away from the screen mid-request? I suspect browser code is pretty robust these days so I doubt it would crash the browser but would it cause any issues with the webapp?
What about React Native? Since this is basically built on top of mobile native code, will I run into this memory crashing problem if I put the callback in the component itself?
If this is a problem, is there a good pattern to use for a central global persistent object to handle callbacks and dispatch the results to any registered components?
Note that I won't be using redux in my code - instead I plan to use a simpler framework (hookstate, hookstate.js.org) that lets me keep track of global state and use it inside components when updated. This seems sortof like a callback system for global state but it isn't quite clear to me the best pattern for incorporating HTTP requests through modules like Axios into it.
Suggestions?

The answer will probably depend heavily on how you are doing state management within your application. However, assuming you are using a version of React Native that supports hooks, something like this might be a useful pattern to get started:
import React, {useState, useEffect} from 'react'
import axios, {CancelToken} from 'axios'
const MyComponent = () => {
const [blah, setBlah] = useState(null)
useEffect(() => {
//Axios comes with support for cancelling. See here
//https://github.com/axios/axios#cancellation
const source = CancelToken.source();
let source = null
axios.get('/some/url', {cancelToken: source.token})
.then(({data}) => {
//If the request is successful, we know the component wasn't unmounted.
//Go ahead and call the state-modifying function.
setBlah(data)
})
.catch((thown) => {
//If something goes wrong, check to see if it is because of a cancel.
//If it is, we probalby don't need to do anything.
if (axios.isCancel(thrown)) {
console.log("Cancelled")
}
else {
//Otherwise, we can handle the error gracefully
}
})
//If you return a function from setEffect, it will be
//executed when the component is about to unmount (componentWillUnmount)
//See https://reactjs.org/docs/hooks-effect.html#example-using-hooks-1
return () => source.cancel()
}, []) //This will only run once (the first time the component renders)
return <pre>{blah}</pre>
}
Essentially, we are using useEffect to trigger the request via Axios. We then utilize Axios' built-in cancel functionality (https://github.com/axios/axios#cancellation). We call the cancel function when the component is unmounted. As a result, the then clause of the axios request won't execute. In this particular case, it doesn't really matter; we are using state internal to the component, so there would be no effect of setBlah running after unmount. However, if say you were using Redux for global state, or some other reducer pattern, it is possible that your then clause would trigger some external action even after unmount.
So, to answer the question of "does it matter"? The answer is, "It depends". If you can keep your state local to the component that issues the request, perhaps not. If, like you allude to, you plan to have some global state and expose ways to modify that state (Redux, or a context that exposes a reducer), then it certainly could. A pattern like this should avoid ever calling the state-modifying function in the event your component is unmounted.
Caveat - I haven't tested this code. Most of it is derived from the links I provided, which hopefully should provide any additional context necessary (or corrections if I got an implementation detail wrong).

Related

Callback in Redux/Redux-Saga is an Anti-pattern?

Let's figure it out,
An user performs a login submission, so app shows instead a
Submit button a Spinner, a self contained state whose help us (isLoading).
Okay, when application send to saga login action we can pass a callback
for set false loading state when login submission has successful or failure.
Some experts will say, manage loading state in reducers, but carry to all whole application
loading state, for some specific action not sounds good.
The problem with callbacks is that the architecture doesn't guarantee that the callback gets called or that it won't get called multiple times. That is because redux actions are essential events - where each event can be handled by 0-n handlers (or sagas in our case).
Of course at the time of writing you know that that particular code is handled exactly once, but for anyone else this might be hard to grasp unless there are strict rules in the project how to handle this.
At the same time, you are right that putting local state to redux store isn't great. I usually deal with this by moving the data logic to its own structure. So e.g. loading collections of items from server is no longer local state of some component bur rather global data state that can be used and reused by multiple parts of the applications. This will also make it easier to have custom caching logic for the data cross whole application etc. However, some local component state in redux is still unavoidable for some specific backend calls.
In terms of future, I saw some attempts at useSaga hook, which would work on top of local useReducer hook and therefore local state, however the implementation for such logic is still limited because the current react hook api lacks certain functionality that is necessary to make sure this works well with react commit phase, render bail outs, reducer reruns etc.

reactjs : Fetching external data

I am learning how to use Reactjs and I read the following post :
https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#fetching-external-data
When componentWillMount is used, it is written that The above code is problematic for both server rendering (where the external data won’t be used) and the upcoming async rendering mode (where the request might be initiated multiple times).
I do not understand :
How the request might be initiated multiple times because
componentWillMount is used only one time.
Why componentDidMount solves this problems. For the server rendering,
the external data won't be used also in the first render call.
According to the React docs, changing your component’s state in componentWillMount will not trigger a re-render. This means that if you make your AJAX call and set the response in your component state, it will not re-render, which means you won’t see your data in the DOM. (Remember that the component was initially created with an initial state which most likely didn’t have the data from your external data/AJAX call response)
You could argue that wouldn’t it be better to do my AJAX call to pull external data before the component mounts for the first time?
It wont be better because you don’t know how much time it will take to do your AJAX call. Your AJAX request could take longer to get the data than the time it takes the component to mount and therefore your data does not show up on your DOM as the component has already rendered and there is no re-rendering happening. Your AJAX request could take longer for any reason - your user is on mobile and has slow internet, some issue with your server is making it slow to return responses, etc...
Best thing to do is make your AJAX call in componentDidMount and make your component handle empty data (probably display a loading spinner) until your AJAX request returns the data, sets it to the component state and triggers a re-render! :)
If you read further down they explain a bit more why componentWillMount is problematic.
The above code is problematic for both server rendering (where the
external data won’t be used) and the upcoming async rendering mode
(where the request might be initiated multiple times).
But these may be rendered moot as react is essentially deprecating that lifecycle function come react 17, and thus currently is renamed to UNSAFE_componentWillMount and not recommended for use, but instead use componentDidMount to make your async data fetches.
Why does componentDidMount fix this?
Because the server is pre-rendering the components/JSX, but you don't want the component to fetch its data until after it is actually mounted and running in a browser.
react component lifecycle docs

Using Redux to display a notification in response to a network request

My web application uses React and Redux and UIKit.
It consists of screens A and B.
Screen A contains a button which - upon pressing - will send an asynchronous network request to post some data to a server.
If a user remains on screen A until a response returns from the server, they will receive confirmation about whether or not the request was successful.
The way I have implemented this using React and Redux is by having a component which is responsible for displaying a confirmation banner. This component listens to changes to a state called postStatus in my Redux store. When the user clicks on the button, 3 Redux actions with statuses PENDING, SUCCESS and ERROR are potentially dispatched. After they are dispatched - they are caught by the reducers which change the postStatus state accordingly. This state then gets mapped to my components properties and it is re-rendered to display a relevant banner.
However, if the user does not remain on screen A until a response returns from the server and navigates to screen B instead - I would like a notification to show up so the user is still aware of the status of the request.
My question is, what would be the most sensible way to implement this behaviour?
Here are a couple of things I can think of:
Create a react component that doesn't actually render anything - it just listens to postState and some extra piece of state that represents which screen the user is on. Implement the componentWillReceiveProps react lifecycle method and if the postState is SUCCESS or ERROR and the other state says that the user is on not on screen A - then call UIKit.notify() to show the notification.
Call UIKit.notify() when dispatching the SUCCESS or ERROR action if the user is not on screen A.
Call UIKit.notify() when reducing the state after being dispatched the SUCCESS or ERROR action if the user is not on screen A.
There are most likely a lot of other solutions so I'm keen to hear some.
I know I am late for the answer, but I stumbled upon this myself so here is what I did.
The way I look at it is that the temporary notification is an application side effect.
In my project I am using redux-saga, but you can achieve this using any other side effects library.
The code (using deleting an user as an example):
...
function* deleteUserSuccess(action) {
yield call(message.success, 'Successful deletion!');
}
const watchDeleteUserSuccess = function* () {
yield takeEvery(types.DELETE_USER_SUCCESS, deleteUserSuccess);
}
...
PROS: It is readable and it works.
CONS: The only downside that I see is that you make your sagas know about your UI library. But you can wrap it in a separate function/file/module to abstract the saga from it.

React architecture

This is more an architect question with react.js in mind.
Where would I place the following business logic ?
Once a user has authenticated and we then have access to their user entity. I would like to check a value on the user object and if null populate it will data I can only get on the client and then patch that object. All the code to get, patch an object are within alt.js stores. But this is business logic and doesn't feel right that it's part of the store.
I have considered a react component that is set as a component on the root react route. But it doesn't feel like it's the right place as it does not render anything.
This is not really a concern of React, like you said it is more of an architectural choice.
Like the previous posts have shared, you could use the React lifecycle methods. Perhaps a better approach is to use a state management library like Redux.
If you have yet to use Redux, I would highly recommend it! Although, it is not always the best choice, it seems to work for 99% of my use cases!
Good luck!
Have you used context before?
You could create a context component with a getUser function. This component will handle all the business logic for the User entitiy.
The beauty of context is that it does not need to be a parent or child of other components.
You simply import the getUser via context and you can user the function. IE any component that needs to know User context can do something like this.
componentDidMount() {
this.context.getUserInfo((data) => this.setState({ user: data }))
}
Another use case for Context would be a notifier. A notifier may need to be used randomly throughout your app, passing down as props whenever needed would be a nightmare.
Instead, create it as context, and import it as needed.
If this sounds like it may be useful, I will provide an example.
Further, you can checkout the react docs

Should I call async API's from a React Redux component? ( a new take on it)

I realize this question has been asked before and this topic has been widely discussed in the Redux community, but I have not seen it approached by this angle: Error messages.
In most examples using React + Redux + some middleware (redux-promise and redux-thunk), external api calls are done inside the action creator. The result of the API call then affects the application state with a success case or error case.
My counter-argument:
The main interested party in the results of an API call is a component, particularly because it's the one that has to often show an error message to the user. Error messages are best set as component state. It's easier to "clean up" on componentWillMount. No need to create an action just to clean up an application level error state.
All API call's should be made from a component and it should decide what action creator to call. Action creators then become JUST that, functions that return objects. No side-effects in them.
Again, I stress that this "take" is based on the fact that most of the time, a component will need to handle error messages anyways. So why not call the api and deal with the error right there? Things go ok, call an action creator. Things go bad, show an error. Also, I don't think there will be duplication of API calls across the application. After all, React tries to enforce modularization and top-down flow of data. Two different components really shouldn't be calling the same api. They could call the same action creator though and that's fine. Think sign up and sign in. Different api endpoints. Same final state (authenticated: true)
Anyway, this is my view on it. I'm hoping that someone with more experience will answer if API calls inside components are a good idea. Thank you.
EDIT: Just created this post on medium, which hopefully explains my argument better
Kind of too open ended to come up with a "solution" but here's a short answer.
First off, what do you mean it's easier to clean up on componentWillMount? Many times api calls are done on an already mounted component like a sign up or login component. The API call happens when the button is clicked, not when it's mounted.
Also, the main reason why API calls are done outside React components (assuming you have a data handling framework like redux) is that the library is used as a View layer. A component renders HTML that declaratively reflects the state of your application. When a login API call fails to authenticate, the application state is what changes, and as a result the View. If you start to handle API responses in your component, you may run into issues with out of sync state.
For example, the user logs in 10 times with the wrong credentials and gets "locked out". How do you handle that error? You'll likely add some logic to handle those errors. And what if other parts of the app need to react to this error? Now you start to fire actions based on those errors and essentially go back to making your API calls entirely from an action creator, which happens to live in your component.
Now, this mostly applies to large applications. It's perfectly reasonable to handle API calls in a component if the application is small enough and state management frameworks like redux just add bloat. If it's a large application, however, I still highly recommend keeping API logic in the action creators.

Resources