Skip to content

Commit a317bd0

Browse files
authored
Flip the arguments of Blocks and make the query optional (facebook#18374)
* Flip the arguments of Blocks and make the query optional * Rename Query to Load
1 parent fc7835c commit a317bd0

File tree

7 files changed

+116
-44
lines changed

7 files changed

+116
-44
lines changed

packages/react-client/src/ReactFlightClient.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ export function reportGlobalError(response: Response, error: Error): void {
152152
}
153153

154154
function readMaybeChunk<T>(maybeChunk: Chunk<T> | T): T {
155-
if ((maybeChunk: any).$$typeof !== CHUNK_TYPE) {
155+
if (maybeChunk == null || (maybeChunk: any).$$typeof !== CHUNK_TYPE) {
156156
// $FlowFixMe
157157
return maybeChunk;
158158
}

packages/react-client/src/__tests__/ReactFlight-test.js

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,15 @@ describe('ReactFlight', () => {
2929
act = ReactNoop.act;
3030
});
3131

32-
function block(query, render) {
32+
function block(render, load) {
3333
return function(...args) {
34-
let curriedQuery = () => {
35-
return query(...args);
34+
if (load === undefined) {
35+
return [Symbol.for('react.server.block'), render];
36+
}
37+
let curriedLoad = () => {
38+
return load(...args);
3639
};
37-
return [Symbol.for('react.server.block'), render, curriedQuery];
40+
return [Symbol.for('react.server.block'), render, curriedLoad];
3841
};
3942
}
4043

@@ -70,8 +73,32 @@ describe('ReactFlight', () => {
7073
});
7174

7275
if (ReactFeatureFlags.enableBlocksAPI) {
73-
it('can transfer a Block to the client and render there', () => {
74-
function Query(firstName, lastName) {
76+
it('can transfer a Block to the client and render there, without data', () => {
77+
function User(props, data) {
78+
return (
79+
<span>
80+
{props.greeting} {typeof data}
81+
</span>
82+
);
83+
}
84+
let loadUser = block(User);
85+
let model = {
86+
User: loadUser('Seb', 'Smith'),
87+
};
88+
89+
let transport = ReactNoopFlightServer.render(model);
90+
let root = ReactNoopFlightClient.read(transport);
91+
92+
act(() => {
93+
let UserClient = root.model.User;
94+
ReactNoop.render(<UserClient greeting="Hello" />);
95+
});
96+
97+
expect(ReactNoop).toMatchRenderedOutput(<span>Hello undefined</span>);
98+
});
99+
100+
it('can transfer a Block to the client and render there, with data', () => {
101+
function load(firstName, lastName) {
75102
return {name: firstName + ' ' + lastName};
76103
}
77104
function User(props, data) {
@@ -81,7 +108,7 @@ describe('ReactFlight', () => {
81108
</span>
82109
);
83110
}
84-
let loadUser = block(Query, User);
111+
let loadUser = block(User, load);
85112
let model = {
86113
User: loadUser('Seb', 'Smith'),
87114
};

packages/react-flight-dom-relay/src/__tests__/ReactFlightDOMRelay-test.internal.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,15 @@ describe('ReactFlightDOMRelay', () => {
4444
return model;
4545
}
4646

47-
function block(query, render) {
47+
function block(render, load) {
4848
return function(...args) {
49-
let curriedQuery = () => {
50-
return query(...args);
49+
if (load === undefined) {
50+
return [Symbol.for('react.server.block'), render];
51+
}
52+
let curriedLoad = () => {
53+
return load(...args);
5154
};
52-
return [Symbol.for('react.server.block'), render, curriedQuery];
55+
return [Symbol.for('react.server.block'), render, curriedLoad];
5356
};
5457
}
5558

@@ -89,7 +92,7 @@ describe('ReactFlightDOMRelay', () => {
8992
});
9093

9194
it.experimental('can transfer a Block to the client and render there', () => {
92-
function Query(firstName, lastName) {
95+
function load(firstName, lastName) {
9396
return {name: firstName + ' ' + lastName};
9497
}
9598
function User(props, data) {
@@ -99,7 +102,7 @@ describe('ReactFlightDOMRelay', () => {
99102
</span>
100103
);
101104
}
102-
let loadUser = block(Query, User);
105+
let loadUser = block(User, load);
103106
let model = {
104107
User: loadUser('Seb', 'Smith'),
105108
};

packages/react-flight-dom-webpack/src/__tests__/ReactFlightDOM-test.js

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ describe('ReactFlightDOM', () => {
6262
};
6363
}
6464

65-
function block(query, render) {
65+
function block(render, load) {
6666
let idx = webpackModuleIdx++;
6767
webpackModules[idx] = {
6868
d: render,
@@ -73,10 +73,13 @@ describe('ReactFlightDOM', () => {
7373
name: 'd',
7474
};
7575
return function(...args) {
76-
let curriedQuery = () => {
77-
return query(...args);
76+
if (load === undefined) {
77+
return [Symbol.for('react.server.block'), render];
78+
}
79+
let curriedLoad = () => {
80+
return load(...args);
7881
};
79-
return [Symbol.for('react.server.block'), 'path/' + idx, curriedQuery];
82+
return [Symbol.for('react.server.block'), 'path/' + idx, curriedLoad];
8083
};
8184
}
8285

@@ -288,7 +291,7 @@ describe('ReactFlightDOM', () => {
288291
reject(e);
289292
};
290293
});
291-
function Query() {
294+
function load() {
292295
if (promise) {
293296
throw promise;
294297
}
@@ -300,8 +303,8 @@ describe('ReactFlightDOM', () => {
300303
function DelayedText({children}, data) {
301304
return <Text>{children}</Text>;
302305
}
303-
let _block = block(Query, DelayedText);
304-
return [_block(), _resolve, _reject];
306+
let loadBlock = block(DelayedText, load);
307+
return [loadBlock(), _resolve, _reject];
305308
}
306309

307310
const [FriendsModel, resolveFriendsModel] = makeDelayedText();

packages/react-reconciler/src/__tests__/ReactBlocks-test.js

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,30 @@ describe('ReactBlocks', () => {
4949
};
5050
});
5151

52+
it.experimental('renders a simple component', () => {
53+
function User(props, data) {
54+
return <div>{typeof data}</div>;
55+
}
56+
57+
function App({Component}) {
58+
return (
59+
<Suspense fallback={'Loading...'}>
60+
<Component name="Name" />
61+
</Suspense>
62+
);
63+
}
64+
65+
let loadUser = block(User);
66+
67+
ReactNoop.act(() => {
68+
ReactNoop.render(<App Component={loadUser()} />);
69+
});
70+
71+
expect(ReactNoop).toMatchRenderedOutput(<div>undefined</div>);
72+
});
73+
5274
it.experimental('prints the name of the render function in warnings', () => {
53-
function Query(firstName) {
75+
function load(firstName) {
5476
return {
5577
name: firstName,
5678
};
@@ -69,7 +91,7 @@ describe('ReactBlocks', () => {
6991
);
7092
}
7193

72-
let loadUser = block(Query, User);
94+
let loadUser = block(User, load);
7395

7496
expect(() => {
7597
ReactNoop.act(() => {
@@ -86,8 +108,8 @@ describe('ReactBlocks', () => {
86108
);
87109
});
88110

89-
it.experimental('renders a component with a suspending query', async () => {
90-
function Query(id) {
111+
it.experimental('renders a component with a suspending load', async () => {
112+
function load(id) {
91113
return {
92114
id: id,
93115
name: readString('Sebastian'),
@@ -102,7 +124,7 @@ describe('ReactBlocks', () => {
102124
);
103125
}
104126

105-
let loadUser = block(Query, Render);
127+
let loadUser = block(Render, load);
106128

107129
function App({User}) {
108130
return (
@@ -128,7 +150,7 @@ describe('ReactBlocks', () => {
128150
it.experimental(
129151
'does not support a lazy wrapper around a chunk',
130152
async () => {
131-
function Query(id) {
153+
function load(id) {
132154
return {
133155
id: id,
134156
name: readString('Sebastian'),
@@ -143,7 +165,7 @@ describe('ReactBlocks', () => {
143165
);
144166
}
145167

146-
let loadUser = block(Query, Render);
168+
let loadUser = block(Render, load);
147169

148170
function App({User}) {
149171
return (
@@ -187,7 +209,7 @@ describe('ReactBlocks', () => {
187209
it.experimental(
188210
'can receive updated data for the same component',
189211
async () => {
190-
function Query(firstName) {
212+
function load(firstName) {
191213
return {
192214
name: firstName,
193215
};
@@ -203,7 +225,7 @@ describe('ReactBlocks', () => {
203225
);
204226
}
205227

206-
let loadUser = block(Query, Render);
228+
let loadUser = block(Render, load);
207229

208230
function App({User}) {
209231
return (

packages/react-server/src/ReactFlightServer.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -191,11 +191,11 @@ export function resolveModelToJSON(
191191
}
192192
}
193193
case '2': {
194-
// Query
195-
let query: () => ReactModel = (value: any);
194+
// Load function
195+
let load: () => ReactModel = (value: any);
196196
try {
197-
// Attempt to resolve the query.
198-
return query();
197+
// Attempt to resolve the data.
198+
return load();
199199
} catch (x) {
200200
if (
201201
typeof x === 'object' &&
@@ -204,12 +204,12 @@ export function resolveModelToJSON(
204204
) {
205205
// Something suspended, we'll need to create a new segment and resolve it later.
206206
request.pendingChunks++;
207-
let newSegment = createSegment(request, query);
207+
let newSegment = createSegment(request, load);
208208
let ping = newSegment.ping;
209209
x.then(ping, ping);
210210
return serializeIDRef(newSegment.id);
211211
} else {
212-
// This query failed, encode the error as a separate row and reference that.
212+
// This load failed, encode the error as a separate row and reference that.
213213
request.pendingChunks++;
214214
let errorId = request.nextChunkId++;
215215
emitErrorChunk(request, errorId, x);

packages/react/src/ReactBlock.js

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ import {
1616
REACT_FORWARD_REF_TYPE,
1717
} from 'shared/ReactSymbols';
1818

19-
type BlockQueryFunction<Args: Iterable<any>, Data> = (...args: Args) => Data;
19+
type BlockLoadFunction<Args: Iterable<any>, Data> = (...args: Args) => Data;
2020
export type BlockRenderFunction<Props, Data> = (
2121
props: Props,
2222
data: Data,
2323
) => React$Node;
2424

2525
type Payload<Props, Args: Iterable<any>, Data> = {
26-
query: BlockQueryFunction<Args, Data>,
26+
load: BlockLoadFunction<Args, Data>,
2727
args: Args,
2828
render: BlockRenderFunction<Props, Data>,
2929
};
@@ -44,20 +44,20 @@ function lazyInitializer<Props, Args: Iterable<any>, Data>(
4444
): BlockComponent<Props, Data> {
4545
return {
4646
$$typeof: REACT_BLOCK_TYPE,
47-
_data: payload.query.apply(null, payload.args),
47+
_data: payload.load.apply(null, payload.args),
4848
_render: payload.render,
4949
};
5050
}
5151

5252
export function block<Args: Iterable<any>, Props, Data>(
53-
query: BlockQueryFunction<Args, Data>,
5453
render: BlockRenderFunction<Props, Data>,
54+
load?: BlockLoadFunction<Args, Data>,
5555
): (...args: Args) => Block<Props> {
5656
if (__DEV__) {
57-
if (typeof query !== 'function') {
57+
if (load !== undefined && typeof load !== 'function') {
5858
console.error(
59-
'Blocks require a query function but was given %s.',
60-
query === null ? 'null' : typeof query,
59+
'Blocks require a load function, if provided, but was given %s.',
60+
load === null ? 'null' : typeof load,
6161
);
6262
}
6363
if (render != null && render.$$typeof === REACT_MEMO_TYPE) {
@@ -97,11 +97,28 @@ export function block<Args: Iterable<any>, Props, Data>(
9797
}
9898
}
9999

100+
if (load === undefined) {
101+
return function(): Block<Props> {
102+
let blockComponent: BlockComponent<Props, void> = {
103+
$$typeof: REACT_BLOCK_TYPE,
104+
_data: undefined,
105+
// $FlowFixMe: Data must be void in this scenario.
106+
_render: render,
107+
};
108+
109+
// $FlowFixMe
110+
return blockComponent;
111+
};
112+
}
113+
114+
// Trick to let Flow refine this.
115+
let loadFn = load;
116+
100117
return function(): Block<Props> {
101118
let args: Args = arguments;
102119

103120
let payload: Payload<Props, Args, Data> = {
104-
query: query,
121+
load: loadFn,
105122
args: args,
106123
render: render,
107124
};

0 commit comments

Comments
 (0)