Giter Site home page Giter Site logo

react-fiber-implementation-notes's Introduction

React Fiber Implementation Notes

I've been trying to wrap my head around Fiber's source code, so I figured I'd share my notes. Will hopefully organize this better once I get a better idea of how everything fits together.

To do this, I'm working with a really simple sample app (shown below) and tracing through Fiber. I've marked up React's source to help with this. I'm sure there's a better way to do this and I'd love suggestions.

To follow along, clone React:

git clone https://github.com/facebook/react.git

The Fiber source code can be found at react/src/renderers/shared/fiber.

ReactDOMFiber can be found at react/src/renderers/dom/fiber.

Sample Step-Through

With the following app code:

const TextBox = ({ onChange }) => (
  <input onChange={ onChange }></input>
);

const Display = ({ text }) => (
  <div>{ text }</div>
)

class App extends Component {
  constructor() {
    super();
    this.state = {
      text: 'type',
    };
  };

  changeText = (event) => {
    const value = event.target.value;
    this.setState((state, props) => ({
      text: value
    }));
  };

  render() {
    return (
      <div>
        <Display text={ this.state.text }/>
        <TextBox onChange = { this.changeText } />
      </div>
    )
  }
}

ReactDOM.render(<App />, document.getElementById('root'))

Trace the initial mounting of the app:

ReactDOM.render(<App />, <div id="root">...</div>)

Arguments

  • element:
{
    type: function App()
    //...
}
  • container:
<div id="root">...</div>
  • callback:
undefined

Execution

renderSubtreeIntoContainer(null, element, container, callback);

ReactDOM.renderSubtreeIntoContainer(null, element, container, callback)

Arguments

  • parentComponent:
null
  • children:
//this is the element retruned from <App />'s render().
{
 type: function App(),
 //...
}
  • containerNode:
<div id="root"></div>
  • callback:
undefined

Execution

First, set container: DOMContainerElement based on containerNode.nodeType.
This is a safety check - if containerNode is a DOCUMENT_NODE, we get the root element of the document.

Our container, <div id="root" /> is an ELEMENT_NODE, so we just assign container = containerNode.

let container: DOMContainerElement = containerNode.nodeType === DOCUMENT_NODE
    ? (containerNode: any).documentElement
    : (containerNode: any);

Then set root equal to container._reactRootContainer. In our case, root === undefined, so we enter the following conditional block.

In the block, we prepare our root for mounting in two steps:

  1. clear existing content from the container.
  2. Create a container with DOMRenderer.createContainer(container)

Then, call DOMRenderer.updateContainer() with our root. Note that we do not batch the update if this is our initial mount, indicated by a nonexistent container._reactRootContainer.

This DOMRenderer.updateContainer() call will reconcile and mount the whole tree (!!!)

let root = container._reactRootContainer;

if (!root) {
    // First clear any existing content.
    while (container.lastChild) {
      container.removeChild(container.lastChild);
    }
    
    const newRoot = DOMRenderer.createContainer(container);
    root = container._reactRootContainer = newRoot;
    
    // Initial mount should not be batched.
    DOMRenderer.unbatchedUpdates(() => {
      DOMRenderer.updateContainer(children, newRoot, parentComponent, callback);
    });
} else {
    DOMRenderer.updateContainer(children, root, parentComponent, callback);
}

ReactFiberReconciler.updateContainer(children, root)

Arguments

  • element:
//this is the element retruned from <App />'s render().
{
 type: function App(),
 //...
}
  • container:
{
 context: null,
 pendingContext: null,
 current: [object Object],
 //...
}
  • updateContainer() also accepts parentComponent and callback arguments, but they are both null here.

Execution

  • call getContextForSubtree(parentComponent). In this case, it returns an empty object.
  • assign context to container.context.
  • call scheduleTopLevelUpdate(current, element)
//parentComponent is null here. getContextForSubtree() returns an empty object.
const context = getContextForSubtree(parentComponent)

//container.context === null
if(container.context === null) container.context = context;
else container.pendingContext = context;

//container.current is the container's current Fiber.
scheduleTopLevelUpdate(container.current, element);

ReactFiberReconciler.scheduleTopLevelUpdate() for <App />

Arguments

  • current
//<App />'s fiber. This is passed to the next call - we do not use any of its properties here.
{
 //...
}
  • element
//this is the element retruned from <App />'s render().
{
 type: function App(),
 //...
}
  • scheduleTopLevelUpdate() also accepts a callback argument, but it is null here.

Execution

  • Retrieve the fiber's priority level for scheduling.
    • In the real source, this also checks if the element is an async wrapper component. If so, it will return LowPriority.
  • Call ReactFiberUpdateQueue.addTopLevelUpdate()
  • Call ReactFiberScheduler.scheduleUpdate()
const priorityLevel = getPriorityContext(current);
const nextState = { element };

addTopLevelUpdate(current, nextState, callback, priorityLevel);
scheduleUpdate(current, priorityLevel);

ReactFiberUpdateQueue.addTopLevelUpdate() for <App />

Arguments

  • fiber
  • partialState
{
 //this is the element retruned from <App />'s render().
 element: {
  type: function App(),
  //...
 }
}
  • priorityLevel
1
  • addTopLevelUpdate() also accepts a callback argument, but it is null here.

Execution

  • Generate an update object and call insertUpdate:
const update = {
    priorityLevel,
    partialState,
    callback,
    isReplace: false,
    isForced: false,
    isTopLevelUnmount,
    next: null,
  };
  
  insertUpdate(fiber, update);

ReactFiberUpdateQueue.insertUpdate() for <App />

Arguments

Execution

react-fiber-implementation-notes's People

Contributors

lukebelliveau avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

xilixinluo

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.