state
The state()
function creates a reactive variable with enhanced primitive-like behavior and returns a getter/setter pair. It enables you to store, read, and update local data with automatic DOM reactivity when values change.
⚠️ Convention: In <script>
blocks, always use .value
when working with primitive states. This is because JavaScript Proxies cannot fully emulate primitive assignment.
✅ In Mustache templates {{ ... }}
, .value
is not required.
Use Cases
Use state()
when you want to bind dynamic data in your templates or react to changes with effect()
. It's perfect for form fields, counters, toggles, and local variables scoped to a section or component.
Syntax
const [count, setCount] = pphp.state(0); // or initialize without destructuring (read-only) // Note: When using the read-only form, you cannot update the state or use it with effect methods that require a setter. pphp.state('theme', 'light');
key
: A string that uniquely identifies the state variable. Must not collide with reserved native objects.initial
: The initial value of the state (can be any type: string, number, boolean, object, etc).- Returns: A tuple
[getter, setter]
where:getter()
returns the current value (for reactivity) and can be used directly in bindings.setter(value)
updates the value, or accepts a function for functional updates.
Access Rules
Context | Primitive (string, number, boolean) | Object / Array | Requires .value? |
---|---|---|---|
{{ mustache bindings }} |
✅ Works | ✅ Works | No |
console.log() |
✅ Prints value directly | ✅ Prints value directly | No |
<script> (arithmetic, if, comparisons) |
❌ Requires .value | ✅ Direct access | Yes (primitives only) |
Strict checks (===, typeof, instanceof) |
❌ Requires .value | ❌ Requires .value | Yes |
.value
, everything works naturally. • In
<script>
blocks, always use .value
for primitives (string, number, boolean).
• Objects/arrays work without .value
for property access, but still require it for JSON / type methods.
Examples
<div>
<p>Hello, {{ name }}</p>
<p>Count: {{ count }}</p>
</div>
<script>
const [name, setName] = pphp.state('John');
const [count, setCount] = pphp.state(0);
export const test = () => {
// ✅ Template literals & logging
console.log(`Hello ${name}`); // Works without .value
console.log(count); // Logs primitive directly
// ⚠️ In script: use .value
if (count.value > 5) {
console.log('Count is greater than 5');
}
// ✅ Update
setCount(prev => prev + 1);
// ⚠️ Strict equality
if (name.value === 'John') {
console.log('Exact match');
}
};
</script>
Objects / Arrays
<div>
<p>User: {{ user.name }}</p>
<p>First item: {{ items[0] }}</p>
</div>
<script>
const [user, setUser] = pphp.state({ id: 1, name: 'Abraham' });
const [items, setItems] = pphp.state(['apple', 'banana']);
console.log(user.name); // Works without .value
console.log(items[0]); // Works without .value
// ❌ Requires .value for inspection
console.log(Object.keys(user.value));
console.log(Array.isArray(items.value));
</script>
.value
.
• Console logging = no .value
.
• Script operations = primitives must use .value
.
• Objects/arrays mostly work without .value
, except for type/inspection methods.
⚠️ Requires .value
- • Strict equality:
name.value === 'John'
- • Type checking:
typeof count.value
- • Instance checking:
user.value instanceof Object
- • JSON methods:
JSON.stringify(user.value)
- • Array methods:
Array.isArray(items.value)
- • Object methods:
Object.keys(user.value)
🎯 Best Practice
Use the enhanced primitive-like behavior for natural JavaScript operations (85% of use cases). Only use .value
when you need strict equality, type checking, or specific built-in functions. In Mustache templates, everything works automatically.
Behavior
- State is automatically scoped to the current component or section.
- If the state key already exists, it reuses the existing state instead of overwriting it.
- State keys are reactive and automatically update bound DOM elements or run dependent
effect()
handlers. - Supports functional updates:
setCount(prev => prev + 1)
. - Throws an error if you use a reserved word like
Object
orArray
. - Enhanced: Automatic primitive conversion using
Symbol.toPrimitive
for natural JavaScript operations.
Traditional Example Usage
<button onclick="increment">
Click Me!
</button>
<p>Count is: {{ count }}</p>
<p>Label: {{ label }}</p>
<script>
const [count, setCount] = pphp.state(0);
const [label, setLabel] = pphp.state('Click the button');
export const increment = () => {
// ✅ Enhanced: Many operations work without .value
console.log(`Current count: ${count}`); // Template literal
console.log(count + 1); // Arithmetic
if (count > 5) { // Comparison
setLabel('Lots of clicks!');
} else {
setLabel(`Clicked ${count + 1} times!`); // Mixed operations
}
setCount(prev => prev + 1);
// ❌ Strict equality still needs .value
if (count.value === 10) {
setLabel('Exactly 10 clicks!');
}
};
</script>
Read-Only Short Form
You can also use the getter without destructuring if you don't need a setter:
<body class="{{ theme }}"></body>
<script>
const theme = pphp.state('dark');
// ✅ Enhanced: Works in many contexts without .value
console.log(`Theme is: ${theme}`); // Template literal
console.log(theme == 'dark'); // Loose equality
if (theme == 'dark') { // Boolean context with conversion
console.log('Dark mode enabled');
}
// ❌ Still need .value for strict operations
console.log(theme.value === 'dark'); // Strict equality
console.log(typeof theme.value); // Type checking
</script>
🚀 New Feature Summary
With enhanced type support, Prisma PHP state variables now behave like native primitives in 85% of JavaScript operations. Use .value
only when you need strict equality (===
), type checking (typeof
), or specific built-in functions. In Mustache bindings, everything works automatically as before.
state object with functions
You can initialize state()
with an object
that also contains its own methods like set
and reset
. This allows encapsulating
related logic directly in the state. With enhanced type support, you can call these methods naturally.
<p>Name: {{ user.name }}</p>
<p>Age: {{ user.age }}</p>
<button class="btn btn-primary btn-sm mt-2" onclick="setUserData">
Set Data
</button>
<button class="btn btn-secondary btn-sm mt-2 ml-2" onclick="resetUser">
Reset
</button>
<script>
const [user, setUser] = pphp.state({
name: "",
age: "",
set(data) {
this.name = data.name;
this.age = data.age;
},
reset() {
this.name = "";
this.age = "";
}
});
export const setUserData = () => {
// ✅ Enhanced: Direct method calls work naturally
user.set({ name: "Abraham", age: "30" });
// ✅ Enhanced: Property access without .value
console.log(`User set to: ${user.name}, age ${user.age}`);
// ✅ Enhanced: Conditional logic without .value
if (user.name && user.age) {
console.log('User data is complete');
}
};
export const resetUser = () => {
user.reset();
// ✅ Enhanced: Check if reset worked
if (!user.name && !user.age) {
console.log('User data cleared');
}
};
</script>
Notice that you can call user.set(...)
and user.reset()
directly on the object returned by state()
. With enhanced type support,
you can also access properties and use the object in JavaScript operations naturally
without .value
in most contexts.