mirror of
https://github.com/zebrajr/react.git
synced 2025-12-06 00:20:04 +01:00
* Enable Yarn workspaces for packages/* * Move src/isomorphic/* into packages/react/src/* * Create index.js stubs for all packages in packages/* This makes the test pass again, but breaks the build because npm/ folders aren't used yet. I'm not sure if we'll keep this structure--I'll just keep working and fix the build after it settles down. * Put FB entry point for react-dom into packages/* * Move src/renderers/testing/* into packages/react-test-renderer/src/* Note that this is currently broken because Jest ignores node_modules, and so Yarn linking makes Jest skip React source when transforming. * Remove src/node_modules It is now unnecessary. Some tests fail though. * Add a hacky workaround for Jest/Workspaces issue Jest sees node_modules and thinks it's third party code. This is a hacky way to teach Jest to still transform anything in node_modules/react* if it resolves outside of node_modules (such as to our packages/*) folder. I'm not very happy with this and we should revisit. * Add a fake react-native package * Move src/renderers/art/* into packages/react-art/src/* * Move src/renderers/noop/* into packages/react-noop-renderer/src/* * Move src/renderers/dom/* into packages/react-dom/src/* * Move src/renderers/shared/fiber/* into packages/react-reconciler/src/* * Move DOM/reconciler tests I previously forgot to move * Move src/renderers/native-*/* into packages/react-native-*/src/* * Move shared code into packages/shared It's not super clear how to organize this properly yet. * Add back files that somehow got lost * Fix the build * Prettier * Add missing license headers * Fix an issue that caused mocks to get included into build * Update other references to src/ * Re-run Prettier * Fix lint * Fix weird Flow violation I didn't change this file but Flow started complaining. Caleb said this annotation was unnecessarily using $Abstract though so I removed it. * Update sizes * Fix stats script * Fix packaging fixtures Use file: instead of NODE_PATH since NODE_PATH. NODE_PATH trick only worked because we had no react/react-dom in root node_modules, but now we do. file: dependency only works as I expect in Yarn, so I moved the packaging fixtures to use Yarn and committed lockfiles. Verified that the page shows up. * Fix art fixture * Fix reconciler fixture * Fix SSR fixture * Rename native packages
292 lines
8.0 KiB
JavaScript
292 lines
8.0 KiB
JavaScript
var React = require('react');
|
|
var ReactFeatureFlags = require('ReactFeatureFlags');
|
|
|
|
var ReactDOM;
|
|
|
|
var AsyncComponent = React.unstable_AsyncComponent;
|
|
|
|
describe('ReactDOMFiberAsync', () => {
|
|
var container;
|
|
|
|
beforeEach(() => {
|
|
container = document.createElement('div');
|
|
ReactDOM = require('react-dom');
|
|
});
|
|
|
|
it('renders synchronously by default', () => {
|
|
var ops = [];
|
|
ReactDOM.render(<div>Hi</div>, container, () => {
|
|
ops.push(container.textContent);
|
|
});
|
|
ReactDOM.render(<div>Bye</div>, container, () => {
|
|
ops.push(container.textContent);
|
|
});
|
|
expect(ops).toEqual(['Hi', 'Bye']);
|
|
});
|
|
|
|
describe('with feature flag disabled', () => {
|
|
beforeEach(() => {
|
|
jest.resetModules();
|
|
ReactFeatureFlags = require('ReactFeatureFlags');
|
|
container = document.createElement('div');
|
|
ReactFeatureFlags.enableAsyncSubtreeAPI = false;
|
|
ReactDOM = require('react-dom');
|
|
});
|
|
|
|
it('renders synchronously', () => {
|
|
ReactDOM.render(
|
|
<AsyncComponent><div>Hi</div></AsyncComponent>,
|
|
container,
|
|
);
|
|
expect(container.textContent).toEqual('Hi');
|
|
|
|
ReactDOM.render(
|
|
<AsyncComponent><div>Bye</div></AsyncComponent>,
|
|
container,
|
|
);
|
|
expect(container.textContent).toEqual('Bye');
|
|
});
|
|
});
|
|
|
|
describe('with feature flag enabled', () => {
|
|
beforeEach(() => {
|
|
jest.resetModules();
|
|
ReactFeatureFlags = require('ReactFeatureFlags');
|
|
container = document.createElement('div');
|
|
ReactFeatureFlags.enableAsyncSubtreeAPI = true;
|
|
ReactDOM = require('react-dom');
|
|
});
|
|
|
|
it('AsyncComponent at the root makes the entire tree async', () => {
|
|
ReactDOM.render(
|
|
<AsyncComponent><div>Hi</div></AsyncComponent>,
|
|
container,
|
|
);
|
|
expect(container.textContent).toEqual('');
|
|
jest.runAllTimers();
|
|
expect(container.textContent).toEqual('Hi');
|
|
|
|
ReactDOM.render(
|
|
<AsyncComponent><div>Bye</div></AsyncComponent>,
|
|
container,
|
|
);
|
|
expect(container.textContent).toEqual('Hi');
|
|
jest.runAllTimers();
|
|
expect(container.textContent).toEqual('Bye');
|
|
});
|
|
|
|
it('updates inside an async tree are async by default', () => {
|
|
let instance;
|
|
class Component extends React.Component {
|
|
state = {step: 0};
|
|
render() {
|
|
instance = this;
|
|
return <div>{this.state.step}</div>;
|
|
}
|
|
}
|
|
|
|
ReactDOM.render(
|
|
<AsyncComponent><Component /></AsyncComponent>,
|
|
container,
|
|
);
|
|
expect(container.textContent).toEqual('');
|
|
jest.runAllTimers();
|
|
expect(container.textContent).toEqual('0');
|
|
|
|
instance.setState({step: 1});
|
|
expect(container.textContent).toEqual('0');
|
|
jest.runAllTimers();
|
|
expect(container.textContent).toEqual('1');
|
|
});
|
|
|
|
it('AsyncComponent creates an async subtree', () => {
|
|
let instance;
|
|
class Component extends React.unstable_AsyncComponent {
|
|
state = {step: 0};
|
|
render() {
|
|
instance = this;
|
|
return <div>{this.state.step}</div>;
|
|
}
|
|
}
|
|
|
|
ReactDOM.render(<div><Component /></div>, container);
|
|
jest.runAllTimers();
|
|
|
|
instance.setState({step: 1});
|
|
expect(container.textContent).toEqual('0');
|
|
jest.runAllTimers();
|
|
expect(container.textContent).toEqual('1');
|
|
});
|
|
|
|
it('updates inside an async subtree are async by default', () => {
|
|
class Component extends React.unstable_AsyncComponent {
|
|
render() {
|
|
return <Child />;
|
|
}
|
|
}
|
|
|
|
let instance;
|
|
class Child extends React.Component {
|
|
state = {step: 0};
|
|
render() {
|
|
instance = this;
|
|
return <div>{this.state.step}</div>;
|
|
}
|
|
}
|
|
|
|
ReactDOM.render(<div><Component /></div>, container);
|
|
jest.runAllTimers();
|
|
|
|
instance.setState({step: 1});
|
|
expect(container.textContent).toEqual('0');
|
|
jest.runAllTimers();
|
|
expect(container.textContent).toEqual('1');
|
|
});
|
|
|
|
it('flushSync batches sync updates and flushes them at the end of the batch', () => {
|
|
let ops = [];
|
|
let instance;
|
|
|
|
class Component extends React.Component {
|
|
state = {text: ''};
|
|
push(val) {
|
|
this.setState(state => ({text: state.text + val}));
|
|
}
|
|
componentDidUpdate() {
|
|
ops.push(this.state.text);
|
|
}
|
|
render() {
|
|
instance = this;
|
|
return <span>{this.state.text}</span>;
|
|
}
|
|
}
|
|
|
|
ReactDOM.render(<Component />, container);
|
|
|
|
instance.push('A');
|
|
expect(ops).toEqual(['A']);
|
|
expect(container.textContent).toEqual('A');
|
|
|
|
ReactDOM.flushSync(() => {
|
|
instance.push('B');
|
|
instance.push('C');
|
|
// Not flushed yet
|
|
expect(container.textContent).toEqual('A');
|
|
expect(ops).toEqual(['A']);
|
|
});
|
|
expect(container.textContent).toEqual('ABC');
|
|
expect(ops).toEqual(['A', 'ABC']);
|
|
instance.push('D');
|
|
expect(container.textContent).toEqual('ABCD');
|
|
expect(ops).toEqual(['A', 'ABC', 'ABCD']);
|
|
});
|
|
|
|
it('flushSync flushes updates even if nested inside another flushSync', () => {
|
|
let ops = [];
|
|
let instance;
|
|
|
|
class Component extends React.Component {
|
|
state = {text: ''};
|
|
push(val) {
|
|
this.setState(state => ({text: state.text + val}));
|
|
}
|
|
componentDidUpdate() {
|
|
ops.push(this.state.text);
|
|
}
|
|
render() {
|
|
instance = this;
|
|
return <span>{this.state.text}</span>;
|
|
}
|
|
}
|
|
|
|
ReactDOM.render(<Component />, container);
|
|
|
|
instance.push('A');
|
|
expect(ops).toEqual(['A']);
|
|
expect(container.textContent).toEqual('A');
|
|
|
|
ReactDOM.flushSync(() => {
|
|
instance.push('B');
|
|
instance.push('C');
|
|
// Not flushed yet
|
|
expect(container.textContent).toEqual('A');
|
|
expect(ops).toEqual(['A']);
|
|
|
|
ReactDOM.flushSync(() => {
|
|
instance.push('D');
|
|
});
|
|
// The nested flushSync caused everything to flush.
|
|
expect(container.textContent).toEqual('ABCD');
|
|
expect(ops).toEqual(['A', 'ABCD']);
|
|
});
|
|
expect(container.textContent).toEqual('ABCD');
|
|
expect(ops).toEqual(['A', 'ABCD']);
|
|
});
|
|
|
|
it('flushSync throws if already performing work', () => {
|
|
class Component extends React.Component {
|
|
componentDidUpdate() {
|
|
ReactDOM.flushSync(() => {});
|
|
}
|
|
render() {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Initial mount
|
|
ReactDOM.render(<Component />, container);
|
|
// Update
|
|
expect(() => ReactDOM.render(<Component />, container)).toThrow(
|
|
'flushSync was called from inside a lifecycle method',
|
|
);
|
|
});
|
|
|
|
it('flushSync flushes updates before end of the tick', () => {
|
|
let ops = [];
|
|
let instance;
|
|
|
|
class Component extends React.unstable_AsyncComponent {
|
|
state = {text: ''};
|
|
push(val) {
|
|
this.setState(state => ({text: state.text + val}));
|
|
}
|
|
componentDidUpdate() {
|
|
ops.push(this.state.text);
|
|
}
|
|
render() {
|
|
instance = this;
|
|
return <span>{this.state.text}</span>;
|
|
}
|
|
}
|
|
|
|
ReactDOM.render(<Component />, container);
|
|
jest.runAllTimers();
|
|
|
|
// Updates are async by default
|
|
instance.push('A');
|
|
expect(ops).toEqual([]);
|
|
expect(container.textContent).toEqual('');
|
|
|
|
ReactDOM.flushSync(() => {
|
|
instance.push('B');
|
|
instance.push('C');
|
|
// Not flushed yet
|
|
expect(container.textContent).toEqual('');
|
|
expect(ops).toEqual([]);
|
|
});
|
|
// Only the active updates have flushed
|
|
expect(container.textContent).toEqual('BC');
|
|
expect(ops).toEqual(['BC']);
|
|
|
|
instance.push('D');
|
|
expect(container.textContent).toEqual('BC');
|
|
expect(ops).toEqual(['BC']);
|
|
|
|
// Flush the async updates
|
|
jest.runAllTimers();
|
|
expect(container.textContent).toEqual('ABCD');
|
|
expect(ops).toEqual(['BC', 'ABCD']);
|
|
});
|
|
});
|
|
});
|