mirror of
https://github.com/facebook/react.git
synced 2026-02-26 17:45:07 +00:00
* [Work-in-progress] Assign expiration times to updates An expiration time represents a time in the future by which an update should flush. The priority of the update is related to the difference between the current clock time and the expiration time. This has the effect of increasing the priority of updates as time progresses, to prevent starvation. This lays the initial groundwork for expiration times without changing any behavior. Future commits will replace work priority with expiration times. * Replace pendingWorkPriority with expiration times Instead of a priority, a fiber has an expiration time that represents a point in the future by which it should render. Pending updates still have priorities so that they can be coalesced. We use a host config method to read the current time. This commit implements everything except that method, which currently returns a constant value. So this just proves that expiration times work the same as priorities when time is frozen. Subsequent commits will show the effect of advancing time. * Triangle Demo should use a class shouldComponentUpdate was removed from functional components. Running the demo shows, now that expiration is enabled, the demo does not starve. (Still won't run smoothly until we add back the ability to resume interrupted work.) * Use a magic value for task expiration time There are a few cases related to sync mode where we need to distinguish between work that is scheduled as task and work that is treated like task because it expires. For example, batchedUpdates. We don't want to perform any work until the end of the batch, regardless of how much time has elapsed. * Use current time to calculate expiration time * Add unit tests for expiration and coalescing * Delete unnecessary abstraction * Move performance.now polyfill to ReactDOMFrameScheduling * Add expiration to fuzz tester * Expiration nits - Rename Done -> NoWork - Use max int32 instead of max safe int - Use bitwise operations instead of Math functions
225 lines
6.6 KiB
HTML
225 lines
6.6 KiB
HTML
<!DOCTYPE html>
|
|
<html style="width: 100%; height: 100%; overflow: hidden">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>Fiber Example</title>
|
|
</head>
|
|
<body>
|
|
<h1>Fiber Example</h1>
|
|
<div id="container">
|
|
<p>
|
|
To install React, follow the instructions on
|
|
<a href="https://github.com/facebook/react/">GitHub</a>.
|
|
</p>
|
|
<p>
|
|
If you can see this, React is <strong>not</strong> working right.
|
|
If you checked out the source from GitHub make sure to run <code>npm run build</code>.
|
|
</p>
|
|
</div>
|
|
<script src="../../build/dist/react.development.js"></script>
|
|
<script src="../../build/dist/react-dom.development.js"></script>
|
|
<script src="https://unpkg.com/babel-standalone@6/babel.js"></script>
|
|
<script type="text/babel">
|
|
var dotStyle = {
|
|
position: 'absolute',
|
|
background: '#61dafb',
|
|
font: 'normal 15px sans-serif',
|
|
textAlign: 'center',
|
|
cursor: 'pointer',
|
|
};
|
|
|
|
var containerStyle = {
|
|
position: 'absolute',
|
|
transformOrigin: '0 0',
|
|
left: '50%',
|
|
top: '50%',
|
|
width: '10px',
|
|
height: '10px',
|
|
background: '#eee',
|
|
};
|
|
|
|
var targetSize = 25;
|
|
|
|
class Dot extends React.Component {
|
|
constructor() {
|
|
super();
|
|
this.state = { hover: false };
|
|
}
|
|
enter() {
|
|
this.setState({
|
|
hover: true
|
|
});
|
|
}
|
|
leave() {
|
|
this.setState({
|
|
hover: false
|
|
});
|
|
}
|
|
render() {
|
|
var props = this.props;
|
|
var s = props.size * 1.3;
|
|
var style = {
|
|
...dotStyle,
|
|
width: s + 'px',
|
|
height: s + 'px',
|
|
left: (props.x) + 'px',
|
|
top: (props.y) + 'px',
|
|
borderRadius: (s / 2) + 'px',
|
|
lineHeight: (s) + 'px',
|
|
background: this.state.hover ? '#ff0' : dotStyle.background
|
|
};
|
|
return (
|
|
<div style={style} onMouseEnter={() => this.enter()} onMouseLeave={() => this.leave()}>
|
|
{this.state.hover ? '*' + props.text + '*' : props.text}
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
class SierpinskiTriangle extends React.Component {
|
|
shouldComponentUpdate(nextProps) {
|
|
var o = this.props;
|
|
var n = nextProps;
|
|
return !(
|
|
o.x === n.x &&
|
|
o.y === n.y &&
|
|
o.s === n.s &&
|
|
o.children === n.children
|
|
);
|
|
}
|
|
render() {
|
|
let {x, y, s, children} = this.props;
|
|
if (s <= targetSize) {
|
|
return (
|
|
<Dot
|
|
x={x - (targetSize / 2)}
|
|
y={y - (targetSize / 2)}
|
|
size={targetSize}
|
|
text={children}
|
|
/>
|
|
);
|
|
return r;
|
|
}
|
|
var newSize = s / 2;
|
|
var slowDown = true;
|
|
if (slowDown) {
|
|
var e = performance.now() + 0.8;
|
|
while (performance.now() < e) {
|
|
// Artificially long execution time.
|
|
}
|
|
}
|
|
|
|
s /= 2;
|
|
|
|
return [
|
|
<SierpinskiTriangle x={x} y={y - (s / 2)} s={s}>
|
|
{children}
|
|
</SierpinskiTriangle>,
|
|
<SierpinskiTriangle x={x - s} y={y + (s / 2)} s={s}>
|
|
{children}
|
|
</SierpinskiTriangle>,
|
|
<SierpinskiTriangle x={x + s} y={y + (s / 2)} s={s}>
|
|
{children}
|
|
</SierpinskiTriangle>,
|
|
];
|
|
}
|
|
}
|
|
|
|
class ExampleApplication extends React.Component {
|
|
constructor() {
|
|
super();
|
|
this.state = {
|
|
seconds: 0,
|
|
useTimeSlicing: true,
|
|
};
|
|
this.tick = this.tick.bind(this);
|
|
this.onTimeSlicingChange = this.onTimeSlicingChange.bind(this);
|
|
}
|
|
componentDidMount() {
|
|
this.intervalID = setInterval(this.tick, 1000);
|
|
}
|
|
tick() {
|
|
if (this.state.useTimeSlicing) {
|
|
// Update is time-sliced.
|
|
ReactDOM.unstable_deferredUpdates(() => {
|
|
this.setState(state => ({ seconds: (state.seconds % 10) + 1 }));
|
|
});
|
|
} else {
|
|
// Update is not time-sliced. Causes demo to stutter.
|
|
this.setState(state => ({ seconds: (state.seconds % 10) + 1 }));
|
|
}
|
|
}
|
|
onTimeSlicingChange(value) {
|
|
this.setState(() => ({ useTimeSlicing: value }));
|
|
}
|
|
componentWillUnmount() {
|
|
clearInterval(this.intervalID);
|
|
}
|
|
render() {
|
|
const seconds = this.state.seconds;
|
|
const elapsed = this.props.elapsed;
|
|
const t = (elapsed / 1000) % 10;
|
|
const scale = 1 + (t > 5 ? 10 - t : t) / 10;
|
|
const transform = 'scaleX(' + (scale / 2.1) + ') scaleY(0.7) translateZ(0.1px)';
|
|
return (
|
|
<div>
|
|
<div>
|
|
<h3>Time-slicing</h3>
|
|
<p>Toggle this and observe the effect</p>
|
|
<Toggle
|
|
onLabel="On"
|
|
offLabel="Off"
|
|
onChange={this.onTimeSlicingChange}
|
|
value={this.state.useTimeSlicing}
|
|
/>
|
|
</div>
|
|
<div style={{ ...containerStyle, transform }}>
|
|
<div>
|
|
<SierpinskiTriangle x={0} y={0} s={1000}>
|
|
{this.state.seconds}
|
|
</SierpinskiTriangle>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
class Toggle extends React.Component {
|
|
constructor(props) {
|
|
super();
|
|
this.onChange = this.onChange.bind(this);
|
|
}
|
|
onChange(event) {
|
|
this.props.onChange(event.target.value === 'on');
|
|
}
|
|
render() {
|
|
const value = this.props.value;
|
|
return (
|
|
<label onChange={this.onChange}>
|
|
<label>
|
|
{this.props.onLabel}
|
|
<input type="radio" name="value" value="on" checked={value} />
|
|
</label>
|
|
<label>
|
|
{this.props.offLabel}
|
|
<input type="radio" name="value" value="off" checked={!value} />
|
|
</label>
|
|
</label>
|
|
);
|
|
}
|
|
}
|
|
|
|
var start = new Date().getTime();
|
|
function update() {
|
|
ReactDOM.render(
|
|
<ExampleApplication elapsed={new Date().getTime() - start} />,
|
|
document.getElementById('container')
|
|
);
|
|
requestAnimationFrame(update);
|
|
}
|
|
requestAnimationFrame(update);
|
|
</script>
|
|
</body>
|
|
</html>
|