139 lines
3.2 KiB
TypeScript
139 lines
3.2 KiB
TypeScript
import React, { useCallback } from 'react'
|
|
import ReactDOM from 'react-dom'
|
|
import TestUtils from 'react-dom/test-utils'
|
|
import { Provider } from 'react-redux'
|
|
import { createStore } from 'redux'
|
|
import { pack } from './pack'
|
|
import { TStateSelector } from './TStateSelector'
|
|
|
|
describe('pack', () => {
|
|
|
|
interface IProps {
|
|
a: number
|
|
b: string
|
|
update(a: number, b: string): {
|
|
payload: {a: number, b: string},
|
|
type: 'CHANGE',
|
|
}
|
|
c: string[]
|
|
}
|
|
|
|
class PureComponent extends React.PureComponent<IProps> {
|
|
update = () => {
|
|
this.props.update(1, 'one')
|
|
}
|
|
render() {
|
|
return (
|
|
<button onClick={this.update}>
|
|
{this.props.a + this.props.b}
|
|
</button>
|
|
)
|
|
|
|
return this.props.a + this.props.b
|
|
}
|
|
}
|
|
|
|
function FunctionalComponent(props: IProps) {
|
|
const update = useCallback(() => props.update(1, 'one'), [])
|
|
|
|
return (
|
|
<button onClick={update}>
|
|
{props.a + props.b}
|
|
</button>
|
|
)
|
|
}
|
|
|
|
type LocalState = Omit<IProps, 'c' | 'update'>
|
|
interface IState {
|
|
localState: LocalState
|
|
}
|
|
|
|
function reduce(
|
|
state: IState = {localState: {a: 0, b: ''}},
|
|
action: any,
|
|
): IState {
|
|
switch (action.type) {
|
|
case 'CHANGE':
|
|
return {
|
|
...state,
|
|
localState: {
|
|
...action.payload,
|
|
},
|
|
}
|
|
default:
|
|
return state
|
|
}
|
|
}
|
|
|
|
function configurePureComponent<State>(
|
|
getLocalState: TStateSelector<State, LocalState>,
|
|
) {
|
|
return pack(
|
|
getLocalState,
|
|
(localState: LocalState) => localState,
|
|
{
|
|
update(a: number, b: string) {
|
|
return {
|
|
payload: {a, b},
|
|
type: 'CHANGE',
|
|
}
|
|
},
|
|
},
|
|
PureComponent,
|
|
)
|
|
}
|
|
|
|
function configureFunctionalComponent<State>(
|
|
getLocalState: TStateSelector<State, LocalState>,
|
|
) {
|
|
return pack(
|
|
getLocalState,
|
|
(localState: LocalState) => localState,
|
|
{
|
|
update(a: number, b: string) {
|
|
return {
|
|
payload: {a, b},
|
|
type: 'CHANGE',
|
|
}
|
|
},
|
|
},
|
|
FunctionalComponent,
|
|
)
|
|
}
|
|
|
|
const PackedPureComponent = configurePureComponent<IState>(
|
|
state => state.localState)
|
|
|
|
const PackedFunctionalComponent = configureFunctionalComponent<IState>(
|
|
state => state.localState)
|
|
|
|
it('creates a connected component', () => {
|
|
const store = createStore(reduce)
|
|
const element = document.createElement('div')!
|
|
ReactDOM.render(
|
|
<Provider store={store}>
|
|
<PackedPureComponent c={['test']} />
|
|
</Provider>,
|
|
element,
|
|
)
|
|
expect(element.textContent).toBe('0')
|
|
TestUtils.Simulate.click(element.querySelector('button')!)
|
|
expect(element.textContent).toBe('1one')
|
|
})
|
|
|
|
it('should work with functional components', () => {
|
|
const store = createStore(reduce)
|
|
const element = document.createElement('div')!
|
|
ReactDOM.render(
|
|
<Provider store={store}>
|
|
<PackedFunctionalComponent c={['test']} />
|
|
</Provider>,
|
|
element,
|
|
)
|
|
expect(element.textContent).toBe('0')
|
|
TestUtils.Simulate.click(element.querySelector('button')!)
|
|
expect(element.textContent).toBe('1one')
|
|
})
|
|
|
|
})
|