Provide public method to get record before relay runs its pre-updater magic
- Dominant language
- Rust
- Stars
- 19k
- Forks
- 1.9k
- PR merge metrics
- No merged PRs in 30d
Description
tl;dr: add `store.getBase = (id) => store.__recordSource.__mutator._base.get(id)` to the API
Often times, I need to know the previous value of a record in the updater, onNext, onCompleted handler.
For example, if a user updates the assignee of a todo item, i need to remove it from the old user & add it to the new user.
Today, that requires doing one of the following:
### Method 1: Update your server schema
```graphql
mutation updateTodo {
todo {
id
assigneeId
}
oldAssigneeId
}
```
This is awful because the client needs shouldn't necessitate a server change
### Method 2: Update it in the view layer
```js
class TodosByUserId extends Component {
static getDerivedStateFromProps(nextProps) {
const nextTodos = nextProps.todos.filter((todo) => todo.assigneeId === nextProps.userId)
return {todos: nextTodos}
}
}
```
This is even worse. View layers should be as stupid as possible.
### Method 3: Overfetch
```graphql
mutation updateTodo {
viewer {
all10000Todos {
id
assigneeId
}
}
}
```
This is better, but it doesn't work for all scenarios. For example, maybe I want to set a client-side flag on the document if the old assignee was Bob & now it's Barbara.
### Method 4: Use relay internals
```js
updater = (payload, store) => {
const todoId = payload.getLinkedRecord('todo').getValue('id')
const oldAssigneeId = store.__recordSource.__mutator.__base.get(todoId).assigneeId
const newAssigneeId = store.get(todoId).getValue('assigneeId')
removedFromAssignee(todoId, oldAssigneeId)
addToAssignee(todoId, newAssigneeId)
if (oldAssigneeId === 'Bob' && newAssigneeId === 'Barbara') {
store.get(todoId).setValue(true, 'wasBobs')
}
}
```
This is amazing & it does everything I want it to do, but it uses a lot of relay internals. It also returns a POJO instead of a proxy, so it's a little dangerous.
### Proposal
```js
const newAssigneeId = store.get(todoId).getValue('assigneeId')
const oldAssigneeId = store.getBase(todoId).getValue('assigneeId')
```
To keep the API safe & consistent, I propose a `RelayReadOnlyRecordProxy` as a subset of `RelayRecordProxy`. It just has the `get*` methods: `getBase(dataID: string): ?ReadOnlyRecordProxy`
Happy to write the PR, just wanna achieve consensus first.
Contributor guide
Research direction
Start in the updater's store API and the existing RelayRecordProxy methods, then trace the referenced __recordSource and __mutator base-record access. Compare the proposed RelayReadOnlyRecordProxy with the get* subset described in the issue. Done means updater handlers can read the previous record through public getBase(id) without relying on Relay internals.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- graphql, javascript, react
- Domain
- api, frontend
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100