Skip to content

Default shipping state, wp-data's isResolving, vs. Form.Props.initialValues  #438

Closed
@tomalec

Description

@tomalec

This is a follow-up from #422 (comment)

Describe the bug:

The problem is visible at https://github.com/woocommerce/google-listings-and-ads/tree/2e5d58ac1f67f01ee33e076d03014c68ac38311d

What we do:

  1. We have a default state of [] -
    rates: [],
    times: [],
  2. EditFreeCampaign fetches shipping rates from the server-side -
    const { data: savedShippingRates } = useShippingRates();
  3. EditFreeCampaign > SetupFreeListing > From renders given shippingRate.

Expected behavior:

From should eventually render data fetched from the server-side.

Actual behavior:

Sometimes, if racy flows align, the user gets the form with no shipping rates. Gets stuck with the initial data [].

Steps to reproduce:

  1. Checkout https://github.com/woocommerce/google-listings-and-ads/tree/2e5d58ac1f67f01ee33e076d03014c68ac38311d
  2. Go to https://gla1.test/wp-admin/admin.php?page=wc-admin&path=%2Fgoogle%2Fedit-free-campaign&programId=123&pageStep=2

Additional details:

To me it looks like the problem is three fold:

Form

The first problem we have is that the <Form> component uses initialValues to create its own internal state, then ignores subsequent changes made to this prop. I believe it's a faulty implementation and something that should be addressed there.

From our (GLA) perspective fixing it there may take too long, as:

  • this is an external component, with its own governance and release flow
  • this is undocumented and untested behavior, therefore other plugins could rely on this as a feature

wp-data & isResolving

We should be able to solve that on our end by not rendering the Form until the data is ready.
But we cannot.

With the way we obtain the data, we cannot distinguish whether the data was already loaded or not.

const { getShippingRates, isResolving } = select( STORE_KEY );
const data = getShippingRates();
const loading = isResolving( 'getShippingRates' );
return {
loading,
data,
};

The loading flag indicates whether the request is taking place, no whether we asked for it.
So from EditFeeCampaign or Form perspective, the timeline would look like this:

  1. default state is []
  2. we call const { data: savedShippingRates, loading } = useShippingRates();. savedShippingRates is [] (default), loading is false
  3. a moment later savedShippingRates is [] (default), loading is true
  4. once data comes savedShippingRates is [...], loading is false

(see https://github.com/woocommerce/google-listings-and-ads/tree/85612854603bd1d36f8d879d064af5d048109c11)
The problem is just by using those two variables savedShippingRates and loading we cannot judge whether it's the default state or the one fetched from the server-side. (empty array [] could be a valid saved state as well)

That means we may need to change the method we fetch the data, to expose information whether it was fetched or not.

Default

I also start to wonder whether [] is a good default/initial state. Before any data is loaded, do we really consider shipping.rates and .times to be an array ready for manipulation? For example, do we want to allow adding and removing items from it? For other entries like settings we have null not {}. I think we may do the same for rates and times

//cc @eason9487 @ecgan as you were supporting me in finding a solution for that.

Metadata

Metadata

Assignees

No one assigned

    Labels

    category: refactorThe issue/PR is related to refactoring.type: technical debt - external devXThis issue/PR suffers from the ergonomics of external tools/dependencies

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions