Apollo Form State Management - reactjs

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.

Related

Is a stateful "Toolbox Class" an antipattern in react?

I have a react application that interacts with a rest interface. The rest interface simply involves listing, filtering, creating and marking simple text entries. Some of these operations require authentication. Many different pages of the application interact with the same parts of that rest interface, so I would like to centralize the access to that interface, basically meaning I would like some sort of object which has methods like .get(filter_params), create(object), mark(object_id), that could be used by all the components to easily interact with the rest interface, handling authentication and errors.
Since the required Auth Info is stored in Context, I was thinking of handling it like this:
function DemoEntriesPage(){
const RMRef = React.createRef();
const [entries, setEntries] = useState({});
//Updates the state as the page is loaded, the user scrolls, new entries are made and entries are marked
const updateState=(newEntries)=>setEntries(Object.assign({},entries,newEntries));
return (
{
// The RestManager can be used to filter,create and mark entries.
// After thats finished, RestManager will call the updateState function
// to update the state of its parent component, so it reflects the updated state
// RestManager would also consume the AuthContext
// RestManagers siblings can just call the RMRef functions as needed:
}
<RestManager url="api.blabla.com/entries" ref={RMRef} updateState{updateState}/>
{/*Creates a Form for creating new unmarked entries*/}
<EntryForm onSubmit={this.RMRef().create} />
{/*Creates a List view for entries, does the inital load, and pages in new entries as the user scrolls down*/}
<InfiniteScrollList initialLoad="true" load={this.RMRef().load} entriesRender=
{
/*For each loaded entry the rendering can be specified, and a click listener for marking is created*/
(e)=>
<div class="border">
<span> {e.text} </span>
<span>{e.marked?"marked":"unmarked" </span>
<button onclick={()=>this.RMRef().mark(e.id) } />
</div>
} entries={entries}/>
)
}
But this approach does have a couple problems. First of all, every component that uses RestManager needs to implement a very similar updateState function, since access to the previous state is required in order to update it.
Second it seams like a bad pattern, since it breaks the one directional data flow of react, since it allows sideways communication between components.
So is this a pattern break?

One loading screen for multiple React Apollo queries

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.

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 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
});

React + Alt: Use data from multiple stores to make a request to DataSource

I am working on an app using React with Alt flux implementation.
Currently, I'm trying to figure out the best way to use data from multiple Alt stores to compose a request to the backend.
Say, I have a SheetDataStore, FiltersStore and a fetchFilteredData method in my DataSource file. To get proper data from backend, i need to pass to fetchFilteredData both some sheet data and filters, but when you call the fetchFilteredData method from one of the stores, you can pass some arguments and the state of just that exact store.
So i see 2 ways of handling this case:
1) I make 2 actions: prepareFilters and fetchData. The prepareFilters is called from a component and gets handled by FilterStore, which prepares all required data and after that calls fetchData action, passing the prepared data as an argument. The SheetDataStore handles the fetchData action and calls the fetchFilteredData having all required data now.
What i don't like here is that it seems to me, Stores should not be calling actions, so that's kind of a hacky solution.
2) I import FilterStore to the SheetDataStore and use FilterStore.getState() inside of one of SheetDataStore methods to get all the data i need. That seems easy, but there might be some pitfalls of coupling the stores like that.
Are there any best practices for such a case? Maybe some of you faced similar issue and can recommend which of the paths to take from your experience?
Do the binding in the component. Say you have FilterComponent then pass the search action SheetDataAction.search to it:
<FilterComponent search={SheetDataAction.search} />
And in the FilterComponent.render() do something like <button onClick={() => this.props.search(this.props.criteria)} />

Resources