One loading screen for multiple React Apollo queries - reactjs

I'm working on a React Native application with Apollo 2.1. So far, everything has come together really nicely.
I have a top level component that allows users to flip through pages of data. That component is agnostic with regard to the data being viewed.
It looks something like this:
<Query query={GET_AUTHORS}>
{(loading, errors, data) =>
<CategoryPager categories={data.authors}>
{(author) => <AuthorDetails author={author} />}
</CategoryPager>
}
</Query>
Author details would execute queries to retrieve the details relevant for its own display. The advantage is I can build some common components and reuse them:
<Query query={GET_ARTICLES}>
{(loading, errors, data) =>
<CategoryPager categories={data.articles}>
{(articles) => <ArticleDetails article={article} />}
</CategoryPager>
}
</Query>
This is a very simplified (and somewhat pseudocoded) example, but in our more complicated scenario, this composition has been pretty awesome. The problem is, now I'd like to have a top level display when any of the queries are loading (or if one of them has an error).
That is, I'd like to show "this screen is loading," or "this screen has encountered an error; click here to try again."
I've struggled to find examples of other people doing this. What's a good way to have a top level component be aware of the loading status of lower level queries, preferably keeping things as loosely coupled as possible?
One thing I tried (that I didn't think through all that well) was that I could use a context to keep track of some top level state. Then, when the queries change states, they can inform the stack of queries through that context. However, you can't change state during a state transition. The query component exposes the onCompleted and onError, but no onStarted or onNetworkStatusChanged events.

Related

declarative composition vs imperative in react js

While designing the application in react js to increase the reusability I have used the Tabs and then passed the tabs and headers something like this
const tabs ={
"tabHeader1": TabContent1,
"tabHeader2": TabContent2
}
<SwipableTabs tabs={tabs} />
Now my confusion arose when I had to render them permission based , In order to avoid if else juggling I have designed a component like below :
<ProtectedAction>
{children}
</ProtectedAction>
my ProtectedAction component will check for permission and will render the children based on that. Which is exactly what react suggest(be declarative).
Now when I see the above example like tabs which is data driven I am forced to use if else again which I wanted to get rid of.
Please suggest if any better approach is possible .

Apollo Form State Management

What is the most conventional way to deal with form state management using the React Apollo Client? There are too many walkthroughs of basic apollo queries and simple todo apps, but none I've seen showing how to deal with realistic forms.
Imagining three different user flows:
Edit an existing entity (update form)
Edit a collection by adding/removing an element (update form)
Create a new entity (create form)
There seem to be a few solutions:
Use a stateful React component. That stateful form is nested within a <Query /> and <Mutation /> or otherwise has access to both data and mutate. On submit, use the mutate function directly to submit the mutation and (if necessary) manually update the cache on success. It works, but requires a more complex stateful component. One of the side effects is that you have to compute state from props (componentWillReceiveProps etc) because the form requires both a query (for the update form, to show current values) and a mutation, and could potentially receive new data after the form is already rendered (on refetch/poll)
Use Apollo-Link-State and #client- scoped queries. This seems to face all the same problems as a stateful component, if not more because of the extra indirection. It is intuitive for a create form/new entity, since that new entity could be saved in the local cache and then sent to the remote API on submit, and would automatically appear in the normal cache on success. However, it's less intuitive for an update form, since now you have two copies of the same object, essentially. The form will also have to switch its source of truth, to originally read from the normal cache to populate the form, then write to/read from the local cache during editing, then possibly revert to the normal cache after submit. Otherwise good explanations like https://www.robinwieruch.de/react-apollo-link-state-tutorial/ don't show how to send in that locally-cached data, and generally focus on data that's only meant to stay local, such as device API results and user configuration.
Write to the authoritative/'normal' apollo cache, right over the api-fetched data, then read it back from the cache to submit it to the remote API. This seems to make more sense for an update form, since there's only one state for that entity, but won't work for a new entity since that entity won't have an id yet (while we could generate a uuid on the client, I would prefer to allow the backend to generate the id) and thus isn't something the Apollo cache can work with, AFAIK. Another problem with this is that the form data the user just submitted will be overwritten on fetch, which could be frustrating if something didn't work.
Appreciate any advice, examples, or input - thanks in advance!
I find apollo-link-state to be more useful for situations when you want to manage state that's entirely client-side and not part of the data you're fetching from your server. I don't think it's well suited for managing component state, especially forms. What works great is combining Apollo with a library like Formik. This reduces the boilerplate and overall complexity and of your component. Here's a rough example:
<Query query={SOME_QUERY} />
{({ loading, data }) => {
if (loading) return <LoadingIndicator/>
return (
<Mutation query={SOME_MUTATION}>
{(mutate) => (
<Formik
initialValues={_.pick(data.someData, ['foo', 'bar'])}
validate={/** optional validation function or schema **/}
onSubmit={values => mutate({ variables: values })}
>
{(formikProps) => (
<YourFormComponent {...formikProps}/>
)}
</Formik>
)}
</Mutation>
)
}}
</Query>
The Formik component's render function gets a ton of props, which are all described here. Your actual form component can be stateless and basically just render what props are passed down to it (values, errors, onChange handlers, etc.). Formik accepts yup schemas for validation, which makes validating inputs a snap as well.

React: Best way to pass data from a generic "data provider" filter to siblings

I am creating an Analytics webpage (think Google Analytics-like) with React.
I have a main component that encapsulates the different pages available (one overview, then specific drilldowns).
function Analytics(props) {
return (
<div id="main" className="analytics">
<AnalyticsFilter />
<div id="content">
<div id="contentsub">
{props.children}
</div>
</div>
</div>
);
}
The AnalyticsFilter is a way to filter the data displayed - it is the same for all the pages, hence why it is included here. The idea is that the components (the props.children) displaying the data would request it from the filter, which would then lazily load the data (and potentially cache it too), since multiple components might display the same data in different ways (charts, tables, top10, etc.).
My question: What is the ideal way to facilitate communication between any components on the page and the AnalyticsFilter component?
The filter will have 4-6 different data sets available (all based on the same filter parameters), but I don't want to query all of them for pages where it is not needed (ie. a specific view that only cares about a specific data set).
As such the components should be able to
Request a specific kind of data set, and
Be automatically updated when the filter parameters are updated.
Everything is open, as I am just starting out (and fairly new to React). The Analytics component here can be easily rewritten to include more functionality if that makes it easier.
I would take a look at the flux flux architecture which fits perfectly with react. A good implementation of that architecture is redux.
The basic concept is that everything the user enters in your views triggers an action which is handled in the dispatchers and saved in the stores. The stores trigger then the view rerender, e.g. the dispatcher applies your filter to the data, saves the filtered data to the store and the view renders that filtered data.

React Router+Redux: how to fetch prior to dispatching a route change, but also respond to the navigation events?

I'm learning to use React + Redux + react-router-redux. The project is a toy project, and there are only two routes:
<Route path="/" component={ItemList} />
<Route path="/:id/" component={ItemView} />
First, I have implemented basic operation: when user clicks on ItemList, I call dispatch(push(/${id}/)). Then, using <Router onChange={...}> I detect the navigation and dispatch an action to fetch item's data from server. When the data arrives I can finally put it in my state and ItemView updates from just saying "Loading..." to a proper item display.
However, I want things shiny and fancy. On click, I want to start fetching data but keep old UI (the item list) until the data arrives, and only then - having the data - quickly switch the route. Just like GitHub or GitLab do. So I had researched a bit, threw in redux-thunk, redux-promise-middleware and react-redux-loading-bar and wrote some code.
In the list component, I have what's essentially onClick={dispatch(getItem(id))} where getItem is:
export function getItem(id) {
return function (dispatch) {
return dispatch({
type: GET_ITEM,
payload: new Promise(resolve => {
fetch(`/${id}/?json`)
.then(response => resolve(response.json()))
})
}).then(() => {
dispatch(push(`/${id}/`));
})
}
}
When the promise is resolved, redux-promise-middleware
generates a 'GET_ITEM_FINISHED' action for me. So, in my reducer,
I just put the data I got from server into state:
...
case 'GET_ITEM_FINISHED':
return Object.assign({}, state, {
item: action.payload.item
});
...
(The code above had evolved a few dozen of trial-and-error iterations, so it's not clean. I know, it could be prettier, sorry about this.)
It seem to work at first glance, but this way fetching data is completely detached from navigation events - and that's bad. Whenever there's a navigation event that happens from any cause other than getItem dispatch (e.g. back/forward buttons), there won't be any fetches and necessary state transitions. This means, the approach I've taken is wrong, on the concept level.
I'm sort of stuck thinking of how to do it. My only thought is that the state should be like a cache that can "hit" (have the item data at navigation event's time) or "miss" (don't have it). When I have the data - like in my code above - I can readily display it. When there's a miss, things get hairy - either a reducer or a component would have to dispatch a "GET_ITEM" action, but while this would probably work, it somehow feels like a bad idea.
Basically, all I want is that red thin "loading" line when navigation's within my UI, but also proper handling of back/forward buttons (or whatever may change location). When user navigates directly to the page (full page load), the backend will make data available in HTML it serves, as store's preloadedState.
What I need is a high-level idea/overview of how such things are generally done properly. Or a pointer to any library that implements such idea, if there is anything readily available.

React Redux: More containers v.s. less containers

As I get further into implementing redux + react into a fairly complex app which depends on many API requests to load a single page, I'm having trouble deciding whether it's better to have a single container component at the root of the page which handles all async stuff and passes props down to dumb components, v.s. having multiple container components which concern themselves only with the data they need, as well as fetching the data they need. I've gone back and forth between these two patterns and found that they each have pros/cons:
If I put a single container component at the top:
pro: All isFetching props and fetchSomeDependency() actions can be handled in one place.
con: the downside which is really annoying is that I find myself having to forward props and callbacks through multiple components, and certain components in the middle of the tree end up being tied up to this.
Here's a visual example of the issue that shows the relationships required props-wise:
<MyContainer
data={this.props.data}
isFetchingData={this.props.isFetchingData}
fetchData={this.props.fetchData}
>
{!isFetchingData &&
<MyModal
someData={props.data}
fetchData={props.fetchData}
>
<MyModalContent
someData={props.data}
fetchData={props.fetchData}
>
<SomethingThatDependsOnData someData={props.someData} />
<SomeButtonThatFetchesData onClick={props.fetchData} />
</MyModalContent>
</MyModal>
}
</MyContainer>
As you can see, <MyModal /> and <MyModalContent /> now need to be concerned with props that have nothing to do with it, seeing as a modal should be able to be re-used and only be concerned with stylistic qualities of a modal.
At first the above seemed great but once I got to 100+ components it all felt very tangled, and I found the complexity of these top-level container components to be too high for my liking, seeing as most of them (in the app I'm working on) depend on responses from 3+ API requests.
Then I decided to try multiple containers:
pro: Completely removes the need to forward props. It still makes sense to do it in some cases, but it's a lot more flexible.
pro: Way easier to refactor. I'm surprised at how I can significantly move around and refactor components without anything breaking, whereas in the other pattern things broke a lot.
pro: The complexity of each container component is much less. My mapStateToProps and mapDispatchToProps is more specific to the purpose of the component it's in.
con: Any component that depends on async stuff will always need to handle isFetching state in itself. This adds complexity that is not necessary in the pattern where its handled in a single container component.
So the main dilemma is that if I use one container, I get this un-necessary complexity in components between the root container and the leaf components. If I use multiple containers, I get more complexity in the leaf components, and end up with buttons that need to worry about isFetching even though a button should not be concerned about that.
I'd like to know if anyone has found a way to avoid both cons, and if so, what is the "rule of thumb" you follow to avoid this?
Thanks!
The way I have always seen it is to have your containers at the top most component of a logical components group other than your root/app component.
So if we have a simple search app that display results and lets assume the component heiarchy is such
<Root> <- setup the app
<App>
<Search/> <- search input
<Results/> <- results table
</App>
</Root>
I would make Search and Results redux aware containers. Because react component are suppose to be composable. You might have other components or pages that need display Results or Search. If you delegate the data fetch and store awareness to the root or app component, it make the components become dependent on each other/app. This make it harder down the line when you have to implement changes, now you have to change all the places that use them.
The exception to this is probably if you do have really tightly coupled logic between components. Even then, I would say then you should create a container that wraps your tightly coupled components since they won't be abled to be used realistically without each other.
Redux author Dan Abramov suggests that you use container components when you need them. That is, once you get to have too many props wiring up and down between components it's time to use containers.
He calls it an "ongoing process of refactoring".
See this article: https://medium.com/#dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0
I wouldn't even consider using a single container approach. It pretty much entirely negates all advantages of redux. There is no need whatsoever to have a state management system if all your state is in one place and all your callbacks are in one place (root component).
I think there's a thin line to walk, though. I'm making an app where I've been at it for about 5 weeks (part time) and it's up to 3000 lines right now. It has 3 levels of view nesting, a routing mechanism i implemented myself, and components that are 10+ levels of nesting deep that need to modify state. I basically have one redux container for each big screen and it works marvelously.
However, if I click on my "clients" view, I get a clients listing which is fine, since my clients view is inside a redux container and gets the list of clients passed as props. However, when I click on one client, I'm really hesitant to do another redux container for the individual client's profile since it's only one additional level of passing props. It seems that depending on the scope of the app, you might want to pass props up to 1-2 levels past the redux container and if it's any more than that, then just create another redux container. Then again, if it's an even more complex app, then the mixing of sometimes using redux containers and some other times not using them could be even worse for maintainability. In short, my opinion is trying to minimize redux containers wherever possible but definitely not at the expense of complex prop chains, since that's the main point of using redux to begin with.
So it's been over 2 years since I've posted this question, and this whole time
I have been consistently working with React/Redux. My general rule of thumb now
is the following: Use more containers, but try to write components in such a way where they don't need to know about isFetching.
For example, here is a typical example of how I would have built a to-do list before:
function Todos({ isFetching, items }) {
if (isFetching) return <div>Loading...</div>
return (
<ul>
{items.map(item =>
<li key={item.id}>...</li>
)}
</ul>
)
}
Now I would do something more like:
function Todos({ items }) {
if (!items.length) return <div>No items!</div>
return (
<ul>
{items.map(item =>
<li key={item.id}>...</li>
)}
</ul>
)
}
This way, you only have to connect the data, and the component has no concerns about states of asynchronous API calls.
Most things can be written this way. I rarely need isFetching, but when I do it is typically because:
I need to prevent, for example, a submit button from being clicked a second time, which makes an API call, in which case the prop should probably be called disableSubmit rather than isFetching, or
I want to explicitly show a loader when something is waiting for an asynchronous response.
Now, you might think, "wouldn't you want to show a loader when items are being fetched in the above todos example?" but in practice, actually I wouldn't.
The reason for this is that in the above example, let's say you were polling for new todos, or when you add a todo, you "refetch" the todos. What would happen in the first example is that every time this happened, the todos would disappear and get replaced with "Loading..." frequently.
However, in the second example that is not concerned with isFetching, the new items are simply appended/removed. This is much better UX in my opinion.
In fact, before posting this, I went through all the UI code for an exchange interface I wrote which is quite complex and did not find a single instance of having to connect isFetching to a container component that I wrote.
You don't have to dispatch AND load your state in the same place.
In other words, your button can dispatch the async request, while another component can check if you're loading.
So for example:
// < SomeButtonThatFetchesData.js>
const mapDispatchToProps = (dispatch) => ({
onLoad: (payload) =>
dispatch({ type: DATA_LOADED, payload })
});
You'll need to have some middleware to handle a loading state. It needs to update isFetching when you're passing an async payload.
For example:
const promiseMiddleware = store => next => action => {
if (isPromise(action.payload)) {
store.dispatch({ type: ASYNC_START, subtype: action.type });
Then you can use it wherever you want:
// <MyContainer.js>
const mapStateToProps = (state) => ({
isFetching: state.isFetching
});
And load the data in your inner nested component:
// <SomethingThatDependsOnData.js>
const mapStateToProps = (state) => ({
someData: state.someData
});

Resources