main
js 201 lines 5.91 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @emails react-core
8 */
9
10 // NOTE: We're explicitly not using JSX here. This is intended to test
11 // the current stack addendum without having source location added by babel.
12
13 'use strict';
14
15 let React;
16 let ReactDOMClient;
17 let act;
18 let assertConsoleErrorDev;
19
20 describe('ReactChildReconciler', () => {
21 beforeEach(() => {
22 jest.resetModules();
23
24 React = require('react');
25 ReactDOMClient = require('react-dom/client');
26 ({act, assertConsoleErrorDev} = require('internal-test-utils'));
27 });
28
29 function createIterable(array) {
30 return {
31 '@@iterator': function () {
32 let i = 0;
33 return {
34 next() {
35 const next = {
36 value: i < array.length ? array[i] : undefined,
37 done: i === array.length,
38 };
39 i++;
40 return next;
41 },
42 };
43 },
44 };
45 }
46
47 function makeIterableFunction(value) {
48 const fn = () => {};
49 fn['@@iterator'] = function iterator() {
50 let timesCalled = 0;
51 return {
52 next() {
53 const done = timesCalled++ > 0;
54 return {done, value: done ? undefined : value};
55 },
56 };
57 };
58 return fn;
59 }
60
61 it('does not treat functions as iterables', async () => {
62 const iterableFunction = makeIterableFunction('foo');
63
64 const container = document.createElement('div');
65 const root = ReactDOMClient.createRoot(container);
66 await act(() => {
67 root.render(
68 <div>
69 <h1>{iterableFunction}</h1>
70 </div>,
71 );
72 });
73 assertConsoleErrorDev([
74 'Functions are not valid as a React child. ' +
75 'This may happen if you return fn instead of <fn /> from render. ' +
76 'Or maybe you meant to call this function rather than return it.\n' +
77 ' <h1>{fn}</h1>\n' +
78 ' in h1 (at **)',
79 ]);
80 const node = container.firstChild;
81
82 expect(node.innerHTML).toContain(''); // h1
83 });
84
85 it('warns for duplicated array keys', async () => {
86 class Component extends React.Component {
87 render() {
88 return <div>{[<div key="1" />, <div key="1" />]}</div>;
89 }
90 }
91
92 const container = document.createElement('div');
93 const root = ReactDOMClient.createRoot(container);
94 await act(() => {
95 root.render(<Component />);
96 });
97 assertConsoleErrorDev([
98 'Encountered two children with the same key, `1`. ' +
99 'Keys should be unique so that components maintain their identity across updates. ' +
100 'Non-unique keys may cause children to be duplicated and/or omitted — ' +
101 'the behavior is unsupported and could change in a future version.\n' +
102 ' in div (at **)\n' +
103 ' in Component (at **)',
104 ]);
105 });
106
107 it('warns for duplicated array keys with component stack info', async () => {
108 class Component extends React.Component {
109 render() {
110 return <div>{[<div key="1" />, <div key="1" />]}</div>;
111 }
112 }
113
114 class Parent extends React.Component {
115 render() {
116 return React.cloneElement(this.props.child);
117 }
118 }
119
120 class GrandParent extends React.Component {
121 render() {
122 return <Parent child={<Component />} />;
123 }
124 }
125
126 const container = document.createElement('div');
127 const root = ReactDOMClient.createRoot(container);
128 await act(() => {
129 root.render(<GrandParent />);
130 });
131 assertConsoleErrorDev([
132 'Encountered two children with the same key, `1`. ' +
133 'Keys should be unique so that components maintain their identity ' +
134 'across updates. Non-unique keys may cause children to be ' +
135 'duplicated and/or omitted — the behavior is unsupported and ' +
136 'could change in a future version.\n' +
137 ' in div (at **)\n' +
138 ' in Component (at **)\n' +
139 ' in GrandParent (at **)',
140 ]);
141 });
142
143 it('warns for duplicated iterable keys', async () => {
144 class Component extends React.Component {
145 render() {
146 return <div>{createIterable([<div key="1" />, <div key="1" />])}</div>;
147 }
148 }
149
150 const container = document.createElement('div');
151 const root = ReactDOMClient.createRoot(container);
152 await act(() => {
153 root.render(<Component />);
154 });
155 assertConsoleErrorDev([
156 'Encountered two children with the same key, `1`. ' +
157 'Keys should be unique so that components maintain their identity ' +
158 'across updates. Non-unique keys may cause children to be ' +
159 'duplicated and/or omitted — the behavior is unsupported and ' +
160 'could change in a future version.\n' +
161 ' in div (at **)\n' +
162 ' in Component (at **)',
163 ]);
164 });
165
166 it('warns for duplicated iterable keys with component stack info', async () => {
167 class Component extends React.Component {
168 render() {
169 return <div>{createIterable([<div key="1" />, <div key="1" />])}</div>;
170 }
171 }
172
173 class Parent extends React.Component {
174 render() {
175 return React.cloneElement(this.props.child);
176 }
177 }
178
179 class GrandParent extends React.Component {
180 render() {
181 return <Parent child={<Component />} />;
182 }
183 }
184
185 const container = document.createElement('div');
186 const root = ReactDOMClient.createRoot(container);
187 await act(() => {
188 root.render(<GrandParent />);
189 });
190 assertConsoleErrorDev([
191 'Encountered two children with the same key, `1`. ' +
192 'Keys should be unique so that components maintain their identity ' +
193 'across updates. Non-unique keys may cause children to be ' +
194 'duplicated and/or omitted — the behavior is unsupported and ' +
195 'could change in a future version.\n' +
196 ' in div (at **)\n' +
197 ' in Component (at **)\n' +
198 ' in GrandParent (at **)',
199 ]);
200 });
201 });