25
loading...
This website collects cookies to deliver better user experience
export const Counter: FC<{
value: number,
onChange: (x: number) => unknown
} = (props) => {
const onCountChange: ChangeEventHandler<HTMLInputElement> = (e) => {
const val = parseInt(e.currentTarget.value, 10);
if (isFinite(val)) {
props.onChange(val);
}
};
const onUp = () => props.onChange(inc);
const onDown = () => props.onChange(dec);
return (
<div>
<input value={value} onChange={onCountChange} />
<button onClick={onUp}>up</button>
<button onClick={onDown}>down</button>
</div>
);
};
// Usage in an App
const App: FC = () => {
const [counterValue, setCounterValue] = useState(0);
return (
<div>
<Counter
value={counterValue}
onChange={setCounterValue} />
</div>
);
};
useState
for useFunState
import {FC, useState} from 'react';
import useFunState from '@fun-land/use-fun-state';
import {FunState} from '@fun-land/fun-state';
export const Counter: FC<{state: FunState<number>>}> = ({state}) => {
const value = state.get();
const onCountChange: ChangeEventHandler<HTMLInputElement> = (e) => {
const val = parseInt(e.currentTarget.value, 10);
if (isFinite(val)) state.set(val);
};
const onUp = () => state.mod(inc);
const onDown = () => state.mod(dec);
return (
<div>
<input value={value} onChange={onCountChange} />
<button onClick={onUp}>up</button>
<button onClick={onDown}>down</button>
</div>
);
};
const App: FC = () => {
const counterState = useFunState(0);
return (
<div>
<Counter
state={counterState} />
</div>
);
};
const App: FC = () => {
const [counters, setCounter] = useState([0, 1, 2, 3, 4]);
return (
<div>
{counters.map((counter, i) => (
<Counter
value={counter}
onChange={(val) => setCounter( counters.map((c, j) => i === j ? val : c))} />
</div>
);
};
import {index} from '@fun-land/accessor';
const App: FC = () => {
const countersState = useFunState([0, 1, 2, 3, 4]);
return (
<div>
{countersState.get().map((_, i) => (
<Counter state={countersState.focus(index(i))} />
)}
</div>
);
};
Counter
expects a FunState<number>
instance, we just need to focus on one. index
is an Accessor that can point to a specific item in an array, so no custom state handling required. We're just connecting wires.mockState
, to ease unit testing.import {render, fireEvent} from '@testing-library/react';
import {mockState} from '@fun-land/fun-state'
describe('Counter', () => {
it('increments state when up button pressed', () => {
const counterState = mockState(0);
const comp = render(<Counter state={counterState} />);
fireEvent.click(comp.getByText('up'));
expect(counterState.get()).toBe(1);
});
});
onCountChange
:const onCountChange = (state: FunState<number>): ChangeEventHandler<HTMLInputElement> = (e) => {
const val = parseInt(e.currentTarget.value, 10);
if (isFinite(val)) state.set(val);
};
...
<input value={value} onChange={onCountChange(state)} />
describe('onCountChange', () => {
it('updates the state if a valid integer is passed', () => {
const counterState = mockState(0);
onCountChange(counterState)({currentTarget: {value: 12}} as ChangeEvent)
expect(counterState.get()).toEqual(12);
});
});
mockState
.