What Are Effects in Formily? Dynamic Form Behavior Explained
Effects in Formily are lifecycle functions that subscribe to field events—such as onFieldValueChange and onFieldInit—to reactively modify form state, trigger side effects, and control dynamic behavior without imperative UI code.
Formily, an open-source form solution by Alibaba, uses effects as its primary mechanism for handling complex, reactive form logic. These functions tap into the form's lifecycle to enable real-time field interactions, validation orchestration, and conditional visibility. Understanding how effects work is essential for building maintainable, data-driven forms in both React and Vue applications.
Understanding Effects in Formily
Effects are callback functions registered during form creation that receive the Form instance as their argument. Inside an effect, you use the form.subscribe method to listen for specific field lifecycle events.
The core event types include:
onFieldInit: Fires when a field component is created and mountedonFieldValueChange: Triggers whenever a field's committed value changesonFieldInputChange: Fires during input value changes (e.g., while typing)onFieldValidate: Executes right before a field runs validation logiconFieldReset: Occurs when a field resets to its initial valueonFieldBlur/onFieldFocus: Triggered when a field loses or gains focus
By registering listeners for these events, you can react to user interactions, modify dependent fields, call external APIs, or control the overall form workflow. The effect runs once during form instantiation and establishes persistent listeners for the form's lifetime.
How Effects Are Implemented in the Core
According to the alibaba/formily source code, the effects system is implemented across several key files in the formily_next branch.
In packages/core/src/models/Form.ts, the Form class stores an effects property. When you call createForm({ effects }), the constructor invokes runEffects(this, this.props.effects) to register your effect functions. The Form class also exposes addEffects and setEffects methods for dynamic registration.
The actual execution logic resides in packages/core/src/effects.ts. This file contains the runEffects runner that iterates over provided effect functions and executes them with the form instance context.
For framework integration, Formily provides convenience hooks:
- React:
packages/react/src/hooks/useFormEffects.tsautomatically registers effects when components mount - Vue:
packages/vue/src/hooks/useFormEffects.tsprovides the same lifecycle binding for Vue components
Practical Examples of Formily Effects
Basic Effect in a Vue Component
In Vue 3, you combine createForm with useFormEffects to register lifecycle listeners. This example automatically calculates an isAdult checkbox when the age field changes:
<script setup>
import { createForm } from '@formily/core'
import { useFormEffects } from '@formily/vue'
const form = createForm({
effects: (formInstance) => {
formInstance.subscribe((field) => {
if (field.path === 'age' && field.value !== undefined) {
formInstance.setFieldState('isAdult', (state) => {
state.value = field.value >= 18
})
}
})
},
})
useFormEffects(form)
</script>
Source: [use-form-effects.vue](https://github.com/alibaba/formily/blob/formily_next/packages/vue/docs/demos/api/hooks/use-form-effects.vue)
Effect in a React Component
The React implementation follows the same pattern using the FormProvider and useFormEffects hook from @formily/react. Here, a tax field updates automatically when price changes:
import { createForm } from '@formily/core'
import { FormProvider, useFormEffects } from '@formily/react'
const form = createForm({
effects: (f) => {
f.subscribe((field) => {
if (field.path === 'price') {
f.setFieldState('tax', (state) => {
state.value = Number(field.value) * 0.2
})
}
})
},
})
export default function MyForm() {
useFormEffects(form)
return (
<FormProvider form={form}>
{/* form fields */}
</FormProvider>
)
}
Source: [useFormEffects.ts](https://github.com/alibaba/formily/blob/formily_next/packages/react/src/hooks/useFormEffects.ts)
Low-Level API with form.subscribe
For imperative control outside of React or Vue components, use the core form.subscribe method directly. This approach works in any JavaScript environment:
import { createForm } from '@formily/core'
const form = createForm()
form.subscribe((field) => {
console.log(`Field ${field.path} changed to`, field.value)
})
Source: [Form.ts](https://github.com/alibaba/formily/blob/formily_next/packages/core/src/models/Form.ts)
Conditional Logic with the Query API
Effects can leverage form.query to perform bulk operations on matching fields. This example shows a field visibility toggle and pattern modification:
form.effects = (f) => {
f.subscribe((field) => {
if (field.path === 'country' && field.value === 'US') {
f.setFieldState('state', (state) => {
state.visible = true
})
}
})
// Bulk update using query API
f.query('address.*').forEach((subField) => {
subField.setPattern('readOnly')
})
}
Source: [effects.ts](https://github.com/alibaba/formily/blob/formily_next/packages/core/src/effects.ts)
Summary
- Effects are lifecycle functions that receive the
Forminstance and register listeners for field events likeonFieldValueChangeandonFieldInit. - Core implementation resides in
packages/core/src/models/Form.ts(storage and execution) andpackages/core/src/effects.ts(runner logic). - Framework hooks such as
useFormEffectsinpackages/react/src/hooks/useFormEffects.tsandpackages/vue/src/hooks/useFormEffects.tssimplify registration in UI components. - Dynamic behavior is achieved by combining
form.subscribe,form.setFieldState, andform.queryto reactively modify fields, control visibility, and trigger side effects.
Frequently Asked Questions
What field lifecycle events can I listen to in Formily effects?
Formily effects support a comprehensive set of field events including onFieldInit (mounting), onFieldValueChange (value updates), onFieldInputChange (input typing), onFieldValidate (pre-validation), onFieldReset (reset actions), and onFieldBlur/onFieldFocus (focus management). These events allow you to hook into every stage of a field's existence to implement reactive logic.
How do I register effects in React and Vue components?
For React, import useFormEffects from @formily/react and call it with your form instance inside the component. For Vue, import useFormEffects from @formily/vue and call it within your setup function. Both hooks are implemented in packages/react/src/hooks/useFormEffects.ts and packages/vue/src/hooks/useFormEffects.ts respectively, and they automatically bind the effect lifecycle to the component.
Can I perform bulk field updates inside a Formily effect?
Yes. Use the form.query API to select multiple fields by path pattern (e.g., 'address.*'), then iterate over the results to apply state changes. This approach, implemented in the core effects system, lets you efficiently modify visibility, pattern, or values across field groups without individual subscriptions.
Where does Formily store and execute effect functions?
Effect functions are stored in the effects property of the Form class defined in packages/core/src/models/Form.ts. During form instantiation, the constructor calls runEffects from packages/core/src/effects.ts to execute each effect with the form instance, establishing the event subscription infrastructure.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →