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 |
this.getStore('name') |
wildflower.getStore('name') |
Stores are global, not per-component |
store.state.value |
store.value |
Store proxy exposes state properties directly |
querySelector('[data-bind="X"]') |
Use id attributes on elements |
data-bind is stripped from the DOM after binding |
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 |
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()
}
}
})