I am watching Paul O Shannessy - Building React From Scratch
And I understand the mounting process very well but I have hard day trying to understand how React update a component and its children
The reconciler controls the update process by this method:
function receiveComponent(component, element) {
let prevElement = component._currentElement;
if (prevElement === element) {
return;
}
component.receiveComponent(element);
}
Component.receiveComponent
receiveComponent(nextElement) {
this.updateComponent(this._currentElement, nextElement);
}
and this is the Component.updateComponent method:
updateComponent(prevElement, nextElement) {
if (prevElement !== nextElement) {
// React would call componentWillReceiveProps here
}
// React would call componentWillUpdate here
// Update instance data
this._currentElement = nextElement;
this.props = nextElement.props;
this.state = this._pendingState;
this._pendingState = null;
let prevRenderedElement = this._renderedComponent._currentElement;
let nextRenderedElement = this.render();
if (shouldUpdateComponent(prevRenderedElement, nextRenderedElement)) {
Reconciler.receiveComponent(this._renderedComponent, nextRenderedElement);
}
}
This is the part of the code that updates the component after state change, and i assume that it should update the children too, but i can't understand how this code achieves that, in the mounting process React instantiate components to dive deeper in the tree but this doesn't happen here, we need to find the first HTML element then we can change our strategy and update that HTML element in another place in the code, and I can't find any way to find any HTML elements this way.
Finding the first HTML is the way to stop this endless recursion and logically this is what I expect from the code, to stop recursion the same way in the mounting process, but in mounting, this demanded component instantiation so we can delegate to the reconciler that will discover that we are dealing with a wrapper instance of an HTML element not a wrapper instance of a custom component then React can place that HTML element in the DOM.
I can't understand how the code works in the update process. this code as I see won't dive deeper in the tree and I think won't update the children and can't let React find the first HTML element so React can update the DOM element, isn't it?
This is the code repo on Github
I created a codesandbox to dig in
Here is the codesandbox I created
and here's a short recording of me opening the debugger and seeing the call stack.
How it works
Starting from where you left off, Component.updateComponent:
updateComponent(prevElement, nextElement) {
//...
if (shouldUpdateComponent(prevRenderedElement, nextRenderedElement)) {
Reconciler.receiveComponent(this._renderedComponent, nextRenderedElement);
//...
in the Component.updateComponent method Reconciler.receiveComponent is called which calls component.receiveComponent(element);
Now, this component refers to this._renderedComponent and is not an instance of Component but of DOMComponentWrapper
and here's the receiveComponent method of DOMComponentWrapper:
receiveComponent(nextElement) {
this.updateComponent(this._currentElement, nextElement);
}
updateComponent(prevElement, nextElement) {
// debugger;
this._currentElement = nextElement;
this._updateDOMProperties(prevElement.props, nextElement.props);
this._updateDOMChildren(prevElement.props, nextElement.props);
}
Then _updateDOMChildren ends up calling the children render method.
here's a call stack from the codesandbox I created to dig in.
How do we end up in DOMComponentWrapper
in the Component's mountComponent method we have:
let renderedComponent = instantiateComponent(renderedElement);
this._renderedComponent = renderedComponent;
and in instantiateComponent we have:
let type = element.type;
let wrapperInstance;
if (typeof type === 'string') {
wrapperInstance = HostComponent.construct(element);
} else if (typeof type === 'function') {
wrapperInstance = new element.type(element.props);
wrapperInstance._construct(element);
} else if (typeof element === 'string' || typeof element === 'number') {
wrapperInstance = HostComponent.constructTextComponent(element);
}
return wrapperInstance;
HostComponent is being injected with DOMComponentWrapper in dilithium.js main file:
HostComponent.inject(DOMComponentWrapper);
HostComponent is only a kind of proxy meant to invert control and allow different Hosts in React.
here's the inject method:
function inject(impl) {
implementation = impl;
}
and the construct method:
function construct(element) {
assert(implementation);
return new implementation(element);
}
When we have no DOMComponentWrapper
If we are updating a chain of Non Host Components like:
const Child = <div>Hello</div>
const Parent = () => <Child />
How does Child get rendered from an update to Parent?
the Parent Component has the following:
_renderedComponent which is an instance of Child(which is also a Component)
renderedComponent has an instance of Child because it gets the type of the "root" Element (the one returned by the render method)
so Reconciler.receiveComponent(this._renderedComponent, nextRenderedElement) will be calling component.receiveComponent(element) of the Child which in turn calls this.updateComponent(this._currentElement, nextElement); (of Child) which calls it's render method (let nextRenderedElement = this.render();)
React completely copy the actual DOM and create the virtual DOM in javascript. In our application whenever we update any of the data that ends up being rendered in our components, React does not rerender the entire DOM. It only affects the thing that matters. So react actually copies the virtual DOM again. This time it applies the changes to the data that got updated.
It will make the change in the red component and then it will compare this virtual DOM to the old DOM. It will see the different part. Then it will apply the DOM changes only to that different component.
The updating phase starts if props or the state changes. If the data at the top level changes:
If it is passing that data down to its children, all the children are going to be rerendered. If the state of the component at the mid-level gets changed:
This time only its children will get rerendered. React will rerender any part of the tree below that node. Because the data that generates the children components' view actually sits at the parent component(mid-level one). But anything above it, the parent or the siblings will not rerender. because data does not affect them. this concept is called Unidirectional Data Flow.
You can see in action in chrome browser. chose the rendering and then enable the painting flushing option
If you make any change on the page, you will see that updated components will be flashed.
UPDATING PHASE
componentWillReceiveProps method is invoked first in the component lifecycle's updating phase. It is called when a component receives new properties from its parent component. With this method we compare the current component's properties using the this.props object with the next component's properties
using the nextElement.props object. Based on this comparison, we can choose to update the component's state using the this.setState() function, which will NOT trigger
an additional render in this scenario.
Note that no matter how many times you call this.setState() in the componentWillReceiveProps() method, it won't trigger any additional renders of that component. React does an internal optimization where it batches the state updates together.
shouldComponentUpdated dictates if the components should rerender or not. By default, all class components will rerender whenever the props they receive or their state change. this method can prevent the default behavior by returning False. In this method, existing props and state values get compared with the next props and state values and return boolean to let React know whether the component should update or not. this method is for performance optimization. If it returns False componentWillUpdate(), render() and componentDidUpdate() wont get called.
The componentWillUpdate() method is called immediately before React updates the DOM. It gets two arguments: nextProps and nextState. You can use these arguments to prepare for the DOM update. However, you cannot use this.setState() in the componentWillUpdate() method.
After calling the componentWillUpdate() method, React invokes the render() method that performs the DOM update. Then, the componentDidUpdate() method is called.
The componentDidUpdate() method is called immediately after React updates the DOM. It gets these two arguments: prevProps and prevState. We use this method to interact with the updated DOM or perform any post-render operations. For example, in a counter example, counter number is increased in componentDidUpdate.
After componentDidUpdate() is called, the updating cycle ends. A new cycle is started when a component's state is updated or a parent component passes new properties. Or when you call the forceUpdate() method, it triggers a new updating cycle, but skips the shouldComponentUpdate() method (this method is for optimization) on a component that
triggered the update. However, shouldComponentUpdate() is called on all the child components as per the usual updating phase. Try to avoid using the forceUpdate() method as much as possible; this will promote your application's maintainability
Another answer might be the structure of the Fiber tree. During execution, react renders a ReactComponent into an object made out of ReactNodes and props. These ReactNodes are assembled into a FiberNode tree (which might be the in memory representation of the virutal dom?).
In the FiberNode tree, depending on the traversal algorithm (children first, sibling first, etc), React always has a single "next" node to continue. So, React will dive deeper into the tree, and update FiberNodes, as it goes along.
If we take the same example,
function App() {
return <div>
<Parent>
<Child01/>
<Child01/>
</Parent>
<Child03/>
</div>
}
function Parent({children}) {
const [state, setState] = useState(0);
return <div>
<button onClick={x => x+1)>click</button>
<Child02 />
{children}
</div>
}
Which React will transform into this FiberNode tree:
node01 = { type: App, return: null, child: node02, sibling: null }
node02 = { type: 'div', return: node01, child: node03, sibling: null }
node03 = { type: Parent, return: node02, child: node05(?), sibling: node04 }
node04 = { type: Child03, return: node02, child: null, sibling: null }
node05 = { type: Child01, return: node03, child: null, sibling: node06 }
node06 = { type: Child01, return: node03, child: null, sibling: null }
// Parent will spawn its own FiberTree,
node10 = { type: 'div', return: node02, child: node11, sibling: null }
node11 = { type: 'button', return: node10, child: null, sibling: node12 }
node12 = { type: Child02, return: node10, child: null, sibling: node05 }
I might have missed something (ie. node03's child might be node10), but the idea is this - React always have a single node (the 'next' node) to render when it traverses the fiber tree.
I think React not re-render parent component first instead of that, React re-render child component first.
Example: A (parent) -> B (child) -> C (child of B)
When A update state C (re-render) -> B -> A
Hey Consider using a Tree data structure for your need, ReactJs follows a unidirectional manner of Updating the state i.e. As soon as the there is a Change in the parent state then all the children which are passed on the props that are residing in the Parent Component are updated once and for all!
Consider using something known as Depth First Search as an algo option which will find you the Node that connects to the parent and once you reach that node , you check for the state and if there is a deviation from the state variables that are shared by the parent you can update them!
Note : This may all seem a bit theoretical but if you could do something remotely close to this thing you will have created a way to update components just how react does!
I found out experimentally that React will only re-render elements if it have to, which is always, except for {children} and React.memo().
Using children correctly, together with batched dom updates makes a very efficient and smooth user experience.
consider this case:
function App() {
return <div>
<Parent>
<Child01/>
<Child01/>
</Parent>
<Child03/>
</div>
}
function Parent({children}) {
const [state, setState] = useState(0);
return <div>
<button onClick={x => x+1)>click</button>
<Child02 />
{children}
</div>
}
when clicking on the button, you will get the following:
- button click
- setState(...), add Parent to dirty list
- start re-rendering all dirty nodes
- Parent rerenders
- Child02 rerenders
- DONE
Note that
Parent (app) and sibling (Child03) nodes will not get re-rendered, or you'll end up with a re-render recursion.
Parent is re-rendered because its state has changed, so its output has to be recalculated.
{children} have not been affected by this change, so it stays the same. (unless a context is involved, but that's a different mechanism).
finally, <Child02 /> has been marked dirty, because that part of the virtual dom has been touched. While it's trivial for us to see it was not effected, the only way React could verify it is by comparing props, which is not done by default!
the only way to prevent Child02 from rendering is wrapping it with React.memo, which might be slower than just re-rendring it.
I'm trying to create a text editor such as Draft.js, which entails using contentEditable. Anyways, I'm using MutationObserver to detect changes and I want to be able to reflect changes to the DOM in the state.
When a component renders to a string, findDOMNode returns a text DOM node containing that value.
I want to compare the mutation target to the rendered dom and reflect those changes in the app state. It would most likely work if I used findDOMNode, however it's deprecated. Is there an alternative? Or, is there a way to use refs to make this happen?
As an example, I have a PElement class:
class PElement extends React.Component {
constructor (props) {
super(props);
this.state = {
children = []
}
/*Parse content props, etc, for children*/
this.ref = React.createRef();
this.compare = this.compare.bind(this);
}
compare (node) {
//This will allow me to check if the target of the mutation was this component.
return this.ref.current === node;
}
render () {
return (<p ref={this.ref}>{this.state.children}</p>);
}
}
As for the Text class:
class Text extends React.Component {
constructor (props) {
super(props);
//No state. This will be lowest level and controlled.
this.ref = React.createRef();
this.compare = this.compare.bind(this);
}
compare (node) {
return this.ref.current === node;
}
render () {
//I don't know how to create a ref to the rendered text node.
return this.props.content;
}
}
}
I tried to create a Text node and use React.cloneElement, though it didn't work.
In the end, I settled on inferring the location of the Text component by mutating the state with each mutation, ie childList by adding or removing components and checking the child nodes of the parent. This way I can know which Component renders which node.
Though, on second thought, I might just prevent input and modify the state in the way which it should be, based on the captured input. It's more in line with the philosophy of React.
I have a react native component which contains both WillFocus and componentDidMount functions.
My Questions is, if I navigate to this component which function is getting triggered first? 'WillFocus' or 'componentDidMount'
Sample code is showing below
class Notifications extends Component {
static navigationOptions = {
header: null
}
constructor(props) {
super(props);
const{navigation}=this.props
this.state = {
highlightHome : true,
highlightNotifications: true,
}
}
willFocus = [this.props.navigation.addListener(
'willFocus',
payload => {
console.log('willFocus')
}
)]
componentDidMount() {
console.log('componentDidMount')
}
}
React Navigation emits events to screen components that subscribe to them.
componentDidMount:
This method is called once all our children Elements and our Component instances are mounted onto the Native UI. When this method is called we now have access to the Native UI (DOM, UIView, etc.), access to our children refs and the ability to potentially trigger a new render pass.
willFocus:
the screen will focus.
By definition, willFocus will be called after ComponentDidMount because it mounted all the UI components.
componentDidMount call first, but in the next time (back or something...) just willFocus call
how to force render our component when props changes?
how to force render parent component when child component's props changes?
i searched a lot but all solutions are deprecated in new React version.
in my any pages (Route exact path="/post/:id" component={post}) for example (siteUrl/post/1) i am getting (:id) from props (this.props.match.params) and that works.
but when i am in single page component (Post) and this Route (siteUrl/post/1) when i am passing new Props to this component (:id).
props will changes but single component and parent component Will not re render...
You may be using componentDidMount method.
componentDidMount() is invoked immediately after a component is
mounted (inserted into the tree). Initialization that requires DOM
nodes should go here. If you need to load data from a remote endpoint,
this is a good place to instantiate the network request.
but you need to use componentDidUpdate.
componentDidUpdate() is invoked immediately after updating occurs.
This method is not called for the initial render.
You can also use state and other React features without writing a class.
read more: https://reactjs.org/docs/hooks-effect.html
To make both parent and child re-render you need to path prop from parent to it's child.
// parent using
<Parent someProp={{someVal}} />
// parent render:
render() {
const { someProp } = this.props
<Child someProp={{someProp}} />
}
this will surely re-render both components, unless you stated another logic in componentShouldUpdate
in your case Router looks like a parent for Parent so you should only path :id as a prop.
Make sure Router is at the top level, right under the App
Important is ,that you initialise the someVal of the child first in the constructor
public static getDerivedStateFromProps(
nextProps,
nextState
) {
let { someVal } = nextProps;
if (nextState.someVal !== someVal) {
return {
initialVal,
someVal: someVal
};
}
return null;
}
After it will rerender on prop changes because the state changes
We need to perform some actions related to DOM elements in different parts of the component hierarchy, on window.onresize and window.onscroll:
1) Moving elements when resizing to mobile resolution
2) Changing classes and styles when scrolling.
The problem is that the elements are in different levels of the React component hierarchy, i.e. nested in different components. Thus if I understand correctly, I can't use refs to access the DOM nodes.
I have:
{someCondition ? <FirstComponent/>: <SecondComponent/>}
Whereas someCondition can change either due to user UI actions, or it could be true from the beginning of the page load.
I've tried using componentDidMount and componentDidUpdate on my FirstComponent, but found that componentDidMount only fires if someCondition is true from the beginning, and componentDidUpdate indeed fires when someCondition changes, but then the required DOM elements (contained in the component) are not ready at that point, and document.getElementById fails.
Adding window.requestAnimationFrame did not help.
I imagine there should be a solid way to do this in React?
Maybe React.findDomNode?
Thank you.
What you are describing is the antithesis of React. It is an approach I would expect when using an imperative jQuery/Dojo based framework.
With React, you are developing functional components that know how to convert state into rendered HTML and you need to approach the problem differently.
For your problem, your window.onresize and window.onscroll callbacks should not try to manipulate the React DOM elements. Instead it should just update state and let React re-render. Your React components should receive this state and render accordingly.
Here's a simple example where the main app listens to these events and updates its state, which triggers a re-render. The app passes the state as props to the children. The children use the props to conditionally change their css class names. They could easily render different html or inline styles as well.
const Component2 = ({someCondition2}) => {
const className = someCondition2 ? "foo" : "bar";
return <div className={className}>Hello</div>;
};
const Component1 = ({someCondition1, someCondition2}) => {
const className = someCondition1 ? "A" : "B";
return (
<div className={className}>
<Component2 someCondition2={someCondition2} />
</div>
);
};
class App extends React.Component {
state = {
someCondition: false,
someCondition2: true,
};
componentDidMount() {
window.onresize = ev => {
const someCondition = ev.xxx;
// re-render with the new state
this.setState({ someCondition });
};
window.onscroll = ev => {
const someCondition2 = ev.xxx;
this.setState({ someCondition2 });
};
}
render() {
const {someCondition, someCondition2} = this.state;
return (
<Component1
someCondition1={someCondition1}
someCondition2={someCondition2} />
);
}
}
React.render(<App />, document.getElementById("container"));