State is usually a term given to a component’s value that changes when an event happens. In this case, both the parent’s count and child count are considered to be states.
We can create a state property to hold any initial state values.
Event handlers need to be able to access state. Unfortunately, they cannot access state right now because this points back to the listening element.
This is correct because this points back to the listener element if you use a normal callback function. We talked about it in the lesson on this.
If we want to access this inside each event handler, we need to change the value of this with bind. In this case, we bind options as this because options is essentially the component itself.
export default function Tiny (options) {
// ...
function _addEventListeners () {
// ...
listenerElement.addEventListener(eventName, options[fn].bind(options))
}
}
}
Once we do this we can access state with this inside the event listener. We can also access other methods and properties.
Changing the state
We can change the state object directly inside the event listener, but nothing will change in the DOM.
But we don’t want to fix the count. We want to let users set the state themselves.
One way to do this is to let them pass in an object that contains the values they want to set. We’ll then loop through every property in this object and assign it back into state.
After assigning the properties back into state, we call _render so the component gets re-rendered.
export default function Tiny (options) {
// ...
options.setState = function (newState) {
const entries = Object.entries(newState)
for (const entry of entries) {
options.state[entry[0]] = entry[1]
}
_render()
}
// ...
}
We can now pass in the new count value into setState.
This works, but the DOM only updates one time. Why? It happened because we didn’t simply change the textContent of the values that need to be changed. We re-rendered the entire component, so event listeners are removed.
To fix this problem, we need to add event listeners after we call _render.
export default function Tiny (options) {
// ...
options.setState = function (newState) {
const entries = Object.entries(newState)
for (const entry of entries) {
options.state[entry[0]] = entry[1]
}
_render()
_addEventListeners()
}
// ...
}
DOM Diffing
Usable frameworks like React and Vue don’t re-render the entire component. It takes too much time from a performance standpoint.
Instead, they use a process called DOM Diffing which checks the differences in the DOM so they change only things that need to be changed.
There are two kinds of DOM Diffing:
Actual DOM Diffing — where you compare your new HTML with the actual HTML in the DOM.
Virtual DOM Diffing — where you compare your new HTML with the current HTML that was saved in JavaScript.
It can be challenging to implement DOM Diffing so we skipped it and chose to re-render the entire component instead. This choice creates performance problems so I don’t recommend using Tiny as a real framework in your projects. But it serves enough to understand what goes behind the scenes when you use a framework.
Finishing up
Let’s finish up the counter component by increasing childCount when the appropriate button is pressed.