Common Mistakes
Frequently encountered mistakes and how to fix them when working with WildflowerJS.
API Mistakes
These are the most common API usage errors:
| Wrong | Right | Why |
|---|---|---|
wildflower.createStore('name', {...}) |
wildflower.store('name', {...}) |
createStore does not exist |
store.state.value |
store.value (preferred idiom) |
Both forms work. The store proxy exposes state properties directly, so the shorthand is the idiomatic read |
Selecting or styling via [data-bind="X"] |
Use id or class attributes |
Binding attributes are an implementation detail: depending on the rendering path they may or may not remain on rendered elements. Never rely on them either way |
Template Mistakes
WildflowerJS uses standard HTML data attributes, not interpolation syntax:
| Wrong | Right | Why |
|---|---|---|
<div data-list="items"><div>...</div></div> |
<div data-list="items"><template>...</template></div> |
data-list requires a <template> child |
{{variable}} or {variable} |
<span data-bind="variable"></span> |
WildflowerJS doesn't use interpolation syntax |
data-if="condition" |
data-show="condition" or data-render="condition" |
data-if doesn't exist in WildflowerJS |
<svg>...<g data-list="ticks"><template>... |
Bind a fixed set of SVG elements, or precompute one <path d> string |
The HTML parser removes or neuters <template> inside <svg> before any script runs; dev builds diagnose it. Table contexts and <select> are fine — <template> is spec-legal there |
| Two computeds (or methods) with the same name in one definition | Rename one; lint with no-dupe-keys |
JavaScript collapses duplicate object keys at parse time — the framework receives only the later one and cannot warn. If a computed silently stops working, grep for a duplicate name before suspecting reactivity |
State & Reactivity Mistakes
Understanding WildflowerJS reactivity prevents unnecessary workarounds:
| Wrong | Right | Why |
|---|---|---|
Always using immutable spreads:this.items = [...items, newItem] |
this.items.push(newItem) |
Direct mutation works for most cases; immutable spread is only needed for top-level array append |
Spreading parent for nested mutation:this.columns = [...this.columns] |
this.columns[0].cards.push(card) |
Deep proxy handles nested mutations automatically |
Reading state behind a short-circuit:return s.field === 'X' && s.id === item.id |
Read both eagerly first:const f = s.field, id = s.id;return f === 'X' && id === item.id |
The proxy only tracks reads that actually happen. JavaScript's && short-circuits and skips reading the right side when the left is falsy, so that dep never gets tracked. Eagerly destructuring at the top of the computed guarantees both fields are tracked. |
a && b or a ? b : c, the WildflowerJS dependency tracker only records reads that the JavaScript engine actually performed. If a is falsy on the first evaluation, b is never read, and the binding doesn't track b as a dependency. Later, when b changes but a hasn't, the binding doesn't wake up. The symptom is usually non-deterministic: the UI updates correctly for some click sequences and silently fails for others. The fix is to read all potentially-relevant state fields eagerly at the top of the computed, before any branching logic. This same pattern exists in every Proxy-based reactive system (Vue, Solid, MobX, Preact Signals); see Conditional reads and dep tracking for the technical explanation.
Store Definition Mistakes
Store methods go at the top level of the definition. There is no actions or methods block:
// ❌ Wrong: methods in an actions block
wildflower.store('counter', {
state: { count: 0 },
actions: {
increment() { this.count++ } // "actions" block doesn't exist
}
})
// ✅ Right: methods at top level
wildflower.store('counter', {
state: { count: 0 },
increment() { this.count++ }
})
Binding Mistakes
WildflowerJS does not have individual data-bind-* attributes for every HTML attribute. Use data-bind-attr or data-bind-style instead:
| Wrong | Right | Why |
|---|---|---|
data-bind-disabled="isDisabled" |
data-bind-attr="{ disabled: isDisabled }" |
data-bind-disabled doesn't exist; use data-bind-attr for HTML attributes |
Using CSS classes for dynamic widths:class="progress-width-75" |
data-bind-style="{ width: pct + '%' }" |
Use data-bind-style for dynamic inline styles |
Method Naming Mistakes
The framework drives certain method names directly. If you define a non-lifecycle method (an action handler or helper) using one of these reserved names, it will run on the framework's schedule, not yours:
Reserved: init, beforeInit, destroy, beforeDestroy, onUpdate, beforeUpdate, onError, tick.
// ❌ Wrong: "tick" is reserved. The framework calls it every
// animation frame for components in the pool loop, not on click.
wildflower.component('counter', {
state: { count: 0 },
tick() { this.count++ }
})
<button data-action="tick">Click</button> <!-- this won't behave as you expect -->
// ✅ Right: name the action something specific.
wildflower.component('counter', {
state: { count: 0 },
increment() { this.count++ }
})
<button data-action="increment">Click</button>
See Lifecycle Hooks for the full list and what each reserved name does.
Interactive Demo
This demo shows the correct patterns in action: a counter, a list with add/remove, and computed properties:
<div data-component="common-mistakes-demo">
<div class="my-3">
<strong>Counter:</strong> <span data-bind="count"></span>
<button class="btn btn-sm btn-primary ms-2" data-action="increment">+1</button>
<button class="btn btn-sm btn-secondary ms-1" data-action="decrement">-1</button>
</div>
<div class="my-3">
<strong>Items:</strong> <span data-bind="itemCount"></span>
<button class="btn btn-sm btn-success ms-2" data-action="addItem">Add Item</button>
<button class="btn btn-sm btn-danger ms-1" data-action="removeLastItem">Remove Last</button>
</div>
<ul class="list-group mb-3" data-list="items">
<template>
<li class="list-group-item d-flex justify-content-between align-items-center">
<span data-bind="name"></span>
<span class="badge bg-primary" data-bind="id"></span>
</li>
</template>
</ul>
<div data-show="hasItems">
<small class="text-muted">Showing <span data-bind="itemCount"></span> item(s).</small>
</div>
</div>
wildflower.component('common-mistakes-demo', {
state: {
count: 0,
items: [
{ id: 1, name: 'First item' },
{ id: 2, name: 'Second item' }
],
nextId: 3
},
computed: {
itemCount() { return this.items.length },
hasItems() { return this.items.length > 0 }
},
increment() { this.count++ },
decrement() { this.count-- },
addItem() {
const id = this.nextId++
this.items.push({ id, name: 'Item ' + id })
},
removeLastItem() {
if (this.items.length > 0) {
this.items.pop()
}
}
})