tonyhb / tectonic

A declarative REST data loader for React and Redux. Docs @

Home Page:https://tonyhb.github.io/tectonic/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

load decorator throwing error when props update

slightlytyler opened this issue · comments

Ok this is a complex one so bear with me... I have a RecordBuilder component which is rendered by a route with path 'some-models/:someModelId'. This component handles the creation of a record when someModelId is new and the update of a record when someModelId is any other value. I've created a container that utilizes load like this:

const selectId = get('match.params.adapterId');

const selectIsNewRecord = createSelector(
  selectId,
  eq('new'),
);

const container = compose(
  load(props => {
    if (selectIsNewRecord(props)) return {};
    return { record: Adapter.getItem({ id: selectId(props) }) };
  }),
  mapProps(props => {
    const id = selectId(props);
    const isNewRecord = selectIsNewRecord(props);
    const createModel = data => props.query(
      {
        model: Adapter,
        body: data,
        queryType: 'CREATE',
      },
      (err, result) => {
        if (!err) {
          props.replace(`/adapters/${result.id}`);
          props.push(`/adapters/${result.id}/operations`);
        }
      },
    );
    const updateModel = data => props.query({
      body: data,
      model: Adapter,
      modelId: id,
      params: { id },
      queryType: 'UPDATE',
    });
    return {
      isNewRecord,
      onSubmit: isNewRecord ? createModel : updateModel,
    };
  }),
  spinnerWhileLoading(props => (
    props.isNewRecord ? false : props.status.record.isPending()
  )),
);

The key part of this is that load function returns an empty object when we're dealing with a new record. Everything works as expected except when handling the callback after createModel. I get the error Uncaught TypeError: Cannot set property 'params' of undefined which points to this block of code

// Assign the props newQueries to this.queries; this is gonna retain
// query statuses for successful queries and not re-query them even if
// the cache is now invalid
Object.keys(newQueries).forEach(function (q) {
  _this2.queries[q].params = newQueries[q].params;
});

It seems that when load is expecting a previous query to already be present but since I return an object previously it fails? Starting to dive into your codebase so let me know and I'm more than happy to help. Or if you have a better way to handle this case I'm all ears.

Also if there is somewhere I can message you before I open an issue that might be user error lemme know

Huh, this is interesting. Going to throw up a test and see what's going on now.

Whenever a component gets re-rendered with new queries we need to ensure that we copy over the internal status to new queries. The resolver checks for the internal status prior to re-querying or checking the cache; if the status is set we know that this particular query was the parent of a request that was successful, therefore we shouldn't re-render even if the cache is stale.

If we didn't do this, any rerender or paint by react would cause the query to make another request. Then, when props update to pass in new data the query would be recreated, causing a loop.

Somewhere in the logic we're creating multiple queries, as you've seen, and expect them to exist for the life of the component (as you've seen).

In our app we use a one-component-per-action type style; in this case we'd have a Create and Update component which delegates to a shared inner form. This normally keeps queries consistent.

Will take a look at this now, prior to starting the manual cache feature :)

BTW, made you a collaborator so that if PRs come in you're free to merge them - as long as the feature is tested, tests pass, code looks solid etc. Need to find a slack or IRC to chat about this soon!

Just a heads up - released a new version of DDC at docker yestarday so work is hella busy. Getting around to this soon, adjusting milestone due dates as necessary