Skip to content

Commit 1d6e2ff

Browse files
authored
Add forwardRef in reflect (#74)
2 parents 3f83dae + 282d838 commit 1d6e2ff

File tree

4 files changed

+63
-5
lines changed

4 files changed

+63
-5
lines changed

src/core/reflect.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,17 @@ export function reflectFactory(context: Context) {
3131
return function reflect<
3232
Props,
3333
Bind extends BindableProps<Props> = BindableProps<Props>,
34-
>(config: ReflectConfig<Props, Bind>): React.FC<PartialBoundProps<Props, Bind>> {
34+
>(
35+
config: ReflectConfig<Props, Bind>,
36+
): React.ExoticComponent<PartialBoundProps<Props, Bind>> {
3537
const { stores, events, data } = sortProps(config);
3638

37-
return (props) => {
39+
return React.forwardRef((props, ref) => {
3840
const storeProps = context.useUnit(stores);
3941
const eventsProps = context.useUnit(events);
4042

4143
const elementProps: Props = Object.assign(
42-
{},
44+
{ ref },
4345
storeProps,
4446
eventsProps,
4547
data,
@@ -58,7 +60,7 @@ export function reflectFactory(context: Context) {
5860
}, []);
5961

6062
return React.createElement(config.view as any, elementProps as any);
61-
};
63+
});
6264
};
6365
}
6466

src/core/types.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,14 @@ export interface Context {
77
useList: typeof useList;
88
}
99

10+
type UnbindableProps = 'key' | 'ref';
11+
1012
type Storify<Prop> = Omit<Store<Prop>, 'updates' | 'reset' | 'on' | 'off' | 'thru'>;
1113

1214
export type BindableProps<Props> = {
13-
[Key in keyof Props]?: Props[Key] extends (_payload: any) => void
15+
[Key in Exclude<keyof Props, UnbindableProps>]?: Props[Key] extends (
16+
payload: any,
17+
) => void
1418
? Storify<Props[Key]> | Props[Key] | Event<void>
1519
: Storify<Props[Key]> | Props[Key];
1620
};

src/no-ssr/reflect.test.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,20 @@ test('component inside', async () => {
145145
expect(inputName.value).toBe('Bob');
146146
});
147147

148+
test('forwardRef', async () => {
149+
const Name = reflect({
150+
view: React.forwardRef((props, ref: React.ForwardedRef<HTMLInputElement>) => {
151+
return <input data-testid="name" ref={ref} />;
152+
}),
153+
bind: {},
154+
});
155+
156+
const ref = React.createRef<HTMLInputElement>();
157+
158+
const container = render(<Name ref={ref} />);
159+
expect(container.getByTestId('name')).toBe(ref.current);
160+
});
161+
148162
describe('hooks', () => {
149163
describe('mounted', () => {
150164
test('callback', () => {

type-tests/types-reflect.tsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,41 @@ import { reflect } from '../src';
122122

123123
expectType<React.FC>(ReflectedButton);
124124
}
125+
126+
// reflect should not allow binding ref
127+
{
128+
const Text = React.forwardRef(
129+
(_: { value: string }, ref: React.ForwardedRef<HTMLSpanElement>) => null,
130+
);
131+
132+
const ReflectedText = reflect({
133+
view: Text,
134+
bind: {
135+
// @ts-expect-error
136+
ref: React.createRef<HTMLSpanElement>(),
137+
},
138+
});
139+
140+
expectType<React.VFC>(ReflectedText);
141+
}
142+
143+
// reflect should pass ref through
144+
{
145+
const $value = createStore<string>('');
146+
const Text = React.forwardRef(
147+
(_: { value: string }, ref: React.ForwardedRef<HTMLSpanElement>) => null,
148+
);
149+
150+
const ReflectedText = reflect({
151+
view: Text,
152+
bind: { value: $value },
153+
});
154+
155+
const App: React.FC = () => {
156+
const ref = React.useRef(null);
157+
158+
return <ReflectedText ref={ref} />;
159+
};
160+
161+
expectType<React.FC>(App);
162+
}

0 commit comments

Comments
 (0)