Skip to content

Commit

Permalink
WIP initial todos test
Browse files Browse the repository at this point in the history
  • Loading branch information
markerikson committed Aug 19, 2023
1 parent e757128 commit bbf7a4f
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 25 deletions.
2 changes: 1 addition & 1 deletion src/hooks/useSelector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ export function createSelectorHook(context = ReactReduxContext): UseSelector {
// console.log('wrappedOnStoreChange')
return onStoreChange()
}
// console.log('Subscribing to store with tracking')
console.log('Subscribing to store with tracking')
return subscription.addNestedSub(wrappedOnStoreChange, {
trigger: 'tracked',
cache: cacheWrapper.current,
Expand Down
32 changes: 16 additions & 16 deletions src/utils/Subscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,25 +39,25 @@ function createListenerCollection() {
},

notify() {
//console.log('Notifying subscribers')
console.log('Notifying subscribers')
batch(() => {
let listener = first
while (listener) {
//console.log('Listener: ', listener)
console.log('Listener: ', listener)
if (listener.trigger == 'tracked') {
if (listener.selectorCache!.cache.needsRecalculation()) {
//console.log('Calling subscriber due to recalc need')
// console.log(
// 'Calling subscriber due to recalc. Revision before: ',
// $REVISION
// )
console.log('Calling subscriber due to recalc need')
console.log(
'Calling subscriber due to recalc. Revision before: ',
$REVISION
)
listener.callback()
//console.log('Revision after: ', $REVISION)
console.log('Revision after: ', $REVISION)
} else {
// console.log(
// 'Skipping subscriber, no recalc: ',
// listener.selectorCache
// )
console.log(
'Skipping subscriber, no recalc: ',
listener.selectorCache
)
}
} else {
listener.callback()
Expand All @@ -83,7 +83,7 @@ function createListenerCollection() {
) {
let isSubscribed = true

//console.log('Adding listener: ', options.trigger)
console.log('Adding listener: ', options.trigger)

let listener: Listener = (last = {
callback,
Expand Down Expand Up @@ -162,14 +162,14 @@ export function createSubscription(
listener: () => void,
options: AddNestedSubOptions = { trigger: 'always' }
) {
//console.log('addNestedSub: ', options)
console.log('addNestedSub: ', options)
trySubscribe(options)
return listeners.subscribe(listener, options)
}

function notifyNestedSubs() {
if (store && trackingNode) {
//console.log('Updating node in notifyNestedSubs')
console.log('Updating node in notifyNestedSubs')
updateNode(trackingNode, store.getState())
}
listeners.notify()
Expand All @@ -187,7 +187,7 @@ export function createSubscription(

function trySubscribe(options: AddNestedSubOptions = { trigger: 'always' }) {
if (!unsubscribe) {
//console.log('trySubscribe, parentSub: ', parentSub)
console.log('trySubscribe, parentSub: ', parentSub)
unsubscribe = parentSub
? parentSub.addNestedSub(handleChangeWrapper, options)
: store.subscribe(handleChangeWrapper)
Expand Down
15 changes: 8 additions & 7 deletions src/utils/autotracking/autotracking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,15 @@ export class TrackingCache {

needsRecalculation() {
if (!this._needsRecalculation) {
this._needsRecalculation = this.revision > this._cachedRevision
this._needsRecalculation =
this.revision > this._cachedRevision || this._cachedRevision === -1
}
// console.log(
// 'Needs recalculation: ',
// this._needsRecalculation,
// this._cachedRevision,
// this._cachedValue
// )
console.log(
'Needs recalculation: ',
this._needsRecalculation,
this._cachedRevision,
this._cachedValue
)
return this._needsRecalculation
}

Expand Down
125 changes: 124 additions & 1 deletion test/hooks/useSelector.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import type {
} from '../../src/index'
import type { FunctionComponent, DispatchWithoutAction, ReactNode } from 'react'
import type { Store, AnyAction, Action } from 'redux'
import { createSlice, configureStore } from '@reduxjs/toolkit'
import { createSlice, configureStore, PayloadAction } from '@reduxjs/toolkit'
import type { UseSelectorOptions } from '../../src/hooks/useSelector'

// disable checks by default
Expand Down Expand Up @@ -1051,6 +1051,129 @@ describe('React', () => {
})
})
})

describe('Auto-tracking behavior checks', () => {
interface Todo {
id: number
name: string
completed: boolean
}

type TodosState = Todo[]

const counterSlice = createSlice({
name: 'counters',
initialState: {
deeply: {
nested: {
really: {
deeply: {
nested: {
c1: { value: 0 },
},
},
},
},
},

c2: { value: 0 },
},
reducers: {
increment1(state) {
// state.c1.value++
state.deeply.nested.really.deeply.nested.c1.value++
},
increment2(state) {
state.c2.value++
},
},
})

const todosSlice = createSlice({
name: 'todos',
initialState: [
{ id: 0, name: 'a', completed: false },
{ id: 1, name: 'b', completed: false },
{ id: 2, name: 'c', completed: false },
] as TodosState,
reducers: {
toggleCompleted(state, action: PayloadAction<number>) {
const todo = state.find((todo) => todo.id === action.payload)
if (todo) {
todo.completed = !todo.completed
}
},
setName(state) {
state[1].name = 'd'
},
},
})

function makeStore() {
return configureStore({
reducer: {
counter: counterSlice.reducer,
todos: todosSlice.reducer,
},
middleware: (gDM) =>
gDM({
serializableCheck: false,
immutableCheck: false,
}),
})
}

type AppStore = ReturnType<typeof makeStore>
let store: AppStore
type RootState = ReturnType<AppStore['getState']>

const useAppSelector: TypedUseSelectorHook<RootState> = useSelector

beforeEach(() => {
store = makeStore()
})

test.only('should correctly handle updates to nested data', async () => {
let itemSelectorCallsCount = 0
let listSelectorCallsCount = 0
function TodoListItem({ todoId }: { todoId: number }) {
console.log('TodoListItem render: ', todoId)
const todo = useAppSelector((state) => {
itemSelectorCallsCount++
return state.todos.find((t) => t.id === todoId)
})!
return (
<div>
{todo.id}: {todo.name} ({todo.completed})
</div>
)
}

function TodoList() {
const todoIds = useAppSelector((state) => {
listSelectorCallsCount++
return state.todos.map((t) => t.id)
})
console.log('TodoList render: ', todoIds)
return (
<>
{todoIds.map((id) => (
<TodoListItem todoId={id} key={id} />
))}
</>
)
}

rtl.render(
<Provider store={store} stabilityCheck="never">
<TodoList />
</Provider>
)

expect(listSelectorCallsCount).toBe(1)
expect(itemSelectorCallsCount).toBe(3)
})
})
})

describe('createSelectorHook', () => {
Expand Down

0 comments on commit bbf7a4f

Please sign in to comment.