Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RED-24] Feature/entity adapter draftable entity state #3669

Conversation

Olesj-Bilous
Copy link
Contributor

@Olesj-Bilous Olesj-Bilous commented Aug 23, 2023

I got the go-ahead for this feature in this issue

Changes were restricted to types and typing in the src/entities directory

I introduced two new types:

DraftableEntityState<T> = EntityState<T> | Draft<EntityState<T>>

DraftableIdSelector<T> = IdSelector<T | Draft<T>>

Draftable Entity State

This type was used to update the methods of EntityStateAdapter<T> interface, e.g. addOne<S extends DraftableEntityState<T>>

(Perhaps we could lift S up to EntityStateAdapter, as S extends DraftableEntityState<T> = EntityState<T>? I left it unchanged for now)

createStateOperator was updated to reflect this change, which eliminated the need for a ts-ignore

const isDraftTyped = isDraft as <T>(value: any) => value is Draft<T>

function createStateOperator<V, R>(
  mutator: (arg: R, state: DraftableEntityState<V>) => void
) {
  return function operation<S extends DraftableEntityState<V>>(
    state: S,
    arg: R | PayloadAction<R>
  ): S {
    //
    // ...
    //
    if (isDraftTyped<EntityState<V>>(state)) {
      runMutator(state)
      return state
    }
    return createNextState(state, runMutator)
  }
}

Draftable Id Selector

The draftable id selector type seems necessary because createUnsortedStateAdapter<T>(selectId: IdSelector<T>) returns an adapter with method updateMany

updateMany is generated within createUnsortedStateAdapter by a createStateOperator(updateManyMutably) call whereby updateManyMutably is set up to invoke a local function takeNewKey(keys, update, state): boolean

takeNewKey gets the original entity from state to see if update specifies a new id value:

const original = state.entities[update.id]
const updated: T = Object.assign({}, original, update.changes)
const newKey = selectIdValue(updated, selectId)
const hasNewKey = newKey !== update.id

However, since I changed the type of state to extend DraftableEntityState, the second line needs to be const updated: T | Draft<T> and the selectId parameter to createUnsortedStateAdapter should be DraftableIdSelector<T>

selectId parameter type to upstream functions createSortedStateAdapter and createEntityAdapter was updated accordingly

Conclusion

While the DraftableEntityState addition seems harmless, the DraftableIdSelector addition seems as though it may force clients to account for the possibility that the model parameter to selectId is Draft<T> when passing selectId as a parameter to createEntityAdapter

However, as evidenced in the existing tests/entity_state.test.ts, in effect:

createEntityAdapter({
  selectId: (book: BookModel) => book.id,
})

The type-updated createEntityAdapter correctly recognizes selectId as DraftableIdSelector<BookModel>

Only if we try to call createEntityAdapter from a generic entitySliceEnhancer do we get an error 'IdSelector<TModel>' is not assignable to type 'DraftableIdSelector<TModel>'

That is to say, only those who opt in to this newly available functionality - by implementing a generic entitySliceEnhancer - will have to take into account that selectId needs to be DraftableIdSelector<T>

If such an entitySliceEnhancer were to be implemented in accordance with the above, a concretely typed IdSelector will be valid TypeScript again

const slice = entitySliceEnhancer({
  name: 'book',
  selectId: (book: BookModel) => book.id
}) // slice is inferred as Slice<EntityState<BookModel>>

function entitySliceEnhancer<T>({
  selectId
}: {
  selectId: DraftableIdSelector<T>
}) {
  const modelAdapter = createEntityAdapter<T>({
    selectId: selectId
  })
  return createSlice({name: '', initialState: modelAdapter.getInitialState()})
}

RED-24

@codesandbox
Copy link

codesandbox bot commented Aug 23, 2023

Review or Edit in CodeSandbox

Open the branch in Web EditorVS CodeInsiders

Open Preview

@codesandbox-ci
Copy link

codesandbox-ci bot commented Aug 23, 2023

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit c8e899d:

Sandbox Source
Vanilla Configuration
Vanilla Typescript Configuration
rsk-github-issues-example Configuration
@examples-query-react/basic Configuration
@examples-query-react/advanced Configuration
@examples-action-listener/counter Configuration
rtk-esm-cra Configuration

@Olesj-Bilous
Copy link
Contributor Author

Olesj-Bilous commented Aug 23, 2023

CI / Test against dist (16.x) failed because I didn't provide an actual test to the suite, I fixed that in the last commit

Edit: For some reason netlify.toml is trying to build docs/api/* files and fails expecting a comma:

TypeScript error in code block in line 13 of /opt/build/repo/docs/api/autoBatchEnhancer.mdx/codeBlock_1/index.ts
5:48:24 PM: ',' expected.

As for the API extractor, I couldn't find a build command in toolkit/package.json that doesn't include --skipExtraction

When I run it manually as yarn rimraf dist && yarn tsc && yarn lint && node scripts/cli.js --local, it crashes on toolkit:

Extracting API types for entry point:  redux-toolkit
Analysis will use the bundled TypeScript version 4.3.4
API extractor crashed:  Error: The expression contains an import() type, which is not yet supported by API Extractor:
  toolkit/dist/listenerMiddleware/index.d.ts:15:41

Also on query:

Extracting API types for entry point:  rtk-query-react
Analysis will use the bundled TypeScript version 4.3.4
API extractor crashed:  Error: Unsupported export "ApiModules":
  toolkit/src/query/apiTypes.ts:18:1

@markerikson
Copy link
Collaborator

@Olesj-Bilous yeah, that docs build issue was due to another PR that got merged, which I've reverted. Also, the api-extractor stuff is stale - we haven't used that in a while :)

@markerikson
Copy link
Collaborator

@Olesj-Bilous After some discussion, we'd like to put this into 2.0 instead of 1.9.x. That does mean this will need to be re-targeted at the v2.0-integration branch, and also updated to deal with the other entity adapter ID changes in that branch. Could you update this accordingly?

@markerikson markerikson added this to the 2.0 milestone Sep 24, 2023
@markerikson markerikson changed the title Feature/entity adapter draftable entity state [RED-24] Feature/entity adapter draftable entity state Sep 24, 2023
@Olesj-Bilous
Copy link
Contributor Author

@Olesj-Bilous After some discussion, we'd like to put this into 2.0 instead of 1.9.x. That does mean this will need to be re-targeted at the v2.0-integration branch, and also updated to deal with the other entity adapter ID changes in that branch. Could you update this accordingly?

@markerikson Alright I'll take care of that over the course of this week

@Olesj-Bilous Olesj-Bilous changed the base branch from master to v2.0-integration September 27, 2023 17:26
@netlify
Copy link

netlify bot commented Sep 27, 2023

Deploy Preview for redux-starter-kit-docs ready!

Name Link
🔨 Latest commit c8e899d
🔍 Latest deploy log https://app.netlify.com/sites/redux-starter-kit-docs/deploys/6519fa227412c60008b6f8cf
😎 Deploy Preview https://deploy-preview-3669--redux-starter-kit-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@markerikson
Copy link
Collaborator

Okay, looks good!

@markerikson markerikson merged commit b1c2381 into reduxjs:v2.0-integration Oct 1, 2023
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants