To make the Total Count component, we need to get the count from both parent and child components. One way to do this is to pass properties down into Total Count. (We call this props for short).
Here’s what Total Count looks like at this point:
When you do this you’ll encounter an error that says Cannot read property 'count' of undefined.
This error occurs because Total Count doesn’t have count and childCount declared in its state. We’ll remove these two properties for now since we’re going to pass props down from the parent component.
Passing props down from parents
We can pass props down with a tiny-props custom attribute. Each prop will contain a property name and a value like this:
To do this, we need to add tiny-props values into the component before we render it.
export default function Tiny (options) {
// ...
function _renderChildComponents (options) {
// ...
for (const compEl of compEls) {
// ...
// Add props here
_render(comp)
_addEventListeners(comp)
}
}
}
To add props, we need to get the prop string with getAttribute
export default function Tiny (options) {
// ...
function _renderChildComponents (options) {
// ...
for (const compEl of compEls) {
// ...
const attribute = compEl.getAttribute('tiny-props')
}
// ...
}
}
If the element contains props, we want to extract these props into a property-value pair. We can use the same code we wrote for the event listeners here.
export default function Tiny (options) {
// ...
function _renderChildComponents (options) {
// ...
for (const compEl of compEls) {
// ...
const attribute = compEl.getAttribute('tiny-props')
if (attribute) {
const props = attribute
.replace('[', '')
.replace(']', '')
.split(',')
.map(l => l.trim())
console.log(props)
}
}
// ...
}
}
As I mentioned before, the downside to passing values via custom attributes is everything gets converted into strings. So we can’t simply accept values via the tiny-props property. We need a way to transfer other types into the child component. We’ll work on this later.
At this point, we can set the property-value pair into a props object.
export default function Tiny (options) {
// ...
function _renderChildComponents (options) {
// ...
for (const compEl of compEls) {
// ...
const attribute = compEl.getAttribute('tiny-props')
if (attribute) {
// ...
comp.props[props[0]] = props[1]
}
}
// ...
}
}
You’ll see an error that says Cannot set property 'parentCount' of undefined.
This error happens because Tiny doesn’t contain a props object yet. We can fix this by passing a props object when we initialize Tiny.
In Tiny, we can check whether the passed value is a property in the component. If that’s the case, we send the property’s value into the child component. Otherwise, we simply send it as a hard-coded string.
export default function Tiny (options) {
// ...
function _renderChildComponents (options) {
// ...
for (const compEl of compEls) {
// ...
const attribute = compEl.getAttribute('tiny-props')
if (attribute) {
// ...
const prop = props[0]
const value = props[1]
if (options[value]) {
comp.props[prop] = options[value]
} else {
comp.props[prop] = value
}
}
}
// ...
}
}
If we tried getting parentCount in totalCount now, we’ll get the string state.count.
This happens because we used the string state.count when searching for a property in the parent component. Since options['state.count']doesn’t exist as a property, the output value becomes a string.
We need to use options['state']['count'] instead of options['state.count'] to extract the actual value.
To do this, we need to split state.count with .. Then, we reduce each item in the array into the eventual property.
export default function Tiny (options) {
// ...
function _renderChildComponents (options) {
// ...
for (const compEl of compEls) {
// ...
const attribute = compEl.getAttribute('tiny-props')
if (attribute) {
// ...
const prop = props[0]
const value = props[1]
.split('.')
.reduce((acc, current) => acc[current], options)
console.log(value)
}
}
// ...
}
}
In this case, we get the value, which is a number.
So we need to check against three cases now:
The user passes in a hardcoded value, which is always converted into a string.
The user passes in a value that matches the component’s property.
The user passes in a nested value like state.count.
This three-way flow makes adding props a bit more complex, so we should put the entire segment into its own function. We’ll call this function _addProps.
function _addProps (comp) {
// ...
}
We make a call to this function in _renderChildComponents.
We don’t need to add props if the element doesn’t contain a tiny-props attribute, so we can use an early return.
export default function Tiny (options) {
// ...
function _addProps (comp) {
// Add props
const attribute = comp.element.getAttribute('tiny-props')
if (!attribute) return
// ...
}
// ...
}
We’re now ready to tackle the three scenarios. Again, they are:
The user passes in a hardcoded value, which is always converted into a string.
The user passes in a value that matches the component’s property.
The user passes in a nested value like state.count.
We can check for a . for the third case, so let’s begin there.
export default function Tiny (options) {
// ...
function _addProps (comp) {
// ...
const prop = props[0]
let value = props[1]
if (value.includes('.')) {
// Third case
return
}
// First and second case
}
// ...
}
For the third case, we need to perform split and reduce then assign the eventual value as props.
export default function Tiny (options) {
// ...
function _addProps (comp) {
// ...
const prop = props[0]
let value = props[1]
if (value.includes('.')) {
value = value
.split('.')
.reduce((acc, current) => acc[current], options)
comp.props[prop] = value
return
}
// First and second cases
}
// ...
}
For the second case, we can check whether options[value] is valid. If it is, we’ll pass that value as props.
export default function Tiny (options) {
// ...
function _addProps (comp) {
// ...
if (options[value]) {
comp.props[prop] = options[value]
return
}
}
// ...
}
For the first case, we’ll simply pass the value as props.
export default function Tiny (options) {
// ...
function _addProps (comp) {
// ...
comp.props[prop] = value
}
// ...
}
The component should work with all three types of props now.