Softanza
DémarrerEnglish
Softanza
Recherche · narration

Softanza Reactive Objects: A Complete Beginner's Tutorial

Imagine you're using a spreadsheet like Excel. When you change a number in one cell, all the connected formulas update instantly without you having to do anything extra. That's the magic of reactive programming! In simple terms, reactive systems make your code "smart" so that when data changes, everything that depends on it updates automatically—like a chain reaction.

par Mansour Ayouni · dans le dépôt depuis le 2025-08-21 · tutoriel · domaine Concurrence et distribution

Cet article est écrit en anglais, comme la bibliothèque.

Introduction to Reactive Systems

Let's compare traditional programming (where you have to manually handle every update) with reactive programming:

Traditional Programming:                Reactive Programming:
[Change Data] ──→ [Manual Updates]      [Change Data] ──→ [Automatic Cascading Updates]
      ↓                                          ↓
[Update UI]                                [UI Updates Automatically]
      ↓                                          ↓
[Update DB]                                [DB Updates Automatically]
      ↓                                          ↓
[Log Changes]                              [Logging Happens Automatically]

In reactive systems, you tell the code once: "Hey, whenever this data changes, do these things." Then, the system takes care of the rest. This saves time, reduces bugs, and makes your programs feel more alive and responsive.

The Softanza Reactive Programming System

Softanza is a user-friendly framework built for the Haro programming language. It helps you create reactive programs easily. Think of it as a toolbox with different tools that work together to handle changes in data, files, timers, and more.

Here's a visual overview of the Softanza system:

┌──────────────────────────────────────────────────────────────────────────────┐
│                       SOFTANZA REACTIVE SYSTEM                               │
├──────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  ┌─────────────────────┐  ┌─────────────────────┐  ┌─────────────────────┐  │
│  │ REACTIVE OBJECTS    │  │ REACTIVE FUNCTIONS  │  │ REACTIVE FILES      │  │
│  │                     │  │                     │  │                     │  │
│  │ • Attributes        │  │ • Auto-execute      │  │ • Watch Changes     │  │
│  │ • Watchers          │  │ • Dependencies      │  │ • Auto-load         │  │
│  │ • Computed          │  │ • Memoization       │  │                     │  │
│  │ • Binding           │  │                     │  │                     │  │
│  └─────────────────────┘  └─────────────────────┘  └─────────────────────┘  │
│                                                                              │
│  ┌─────────────────────┐  ┌─────────────────────┐  ┌─────────────────────┐  │
│  │ REACTIVE STREAMS    │  │ REACTIVE TIMERS     │  │ REACTIVE HTTP       │  │
│  │                     │  │                     │  │                     │  │
│  │ • Data Flows        │  │ • Schedules         │  │ • Requests          │  │
│  │ • Transformations  │  │ • Intervals         │  │ • Responses         │  │
│  │ • Filtering         │  │ • Timeouts          │  │ • Real-time         │  │
│  │                     │  │                     │  │                     │  │
│  └─────────────────────┘  └─────────────────────┘  └─────────────────────┘  │
│                                                                              │
├──────────────────────────────────────────────────────────────────────────────┤
│                            libuv Foundation                                  │
│                 (Same as used in Node.js and Ring.js)                        │
└──────────────────────────────────────────────────────────────────────────────┘

This tutorial zooms in on Reactive Objects, the building blocks of Softanza. We'll cover the other parts (like Reactive Functions or Streams) in future guides. For now, let's focus on making objects "reactive" so they respond to changes effortlessly.

Reactive Objects: Core Concepts

A Reactive Object is like a regular object but with superpowers. It notices when its properties (called attributes) change and alerts anything watching them. It's perfect for apps where data updates need to trigger actions, like updating a user's profile.

Basic Structure:

┌────────────────────────────────────────────────────┐
│                REACTIVE OBJECT                     │
├────────────────────────────────────────────────────┤
│ Attributes (Data):                                 │
│ • @Name = "John"                                   │
│ • @Age = 25                                        │
│ • @Email = "john@test.com"                         │
├────────────────────────────────────────────────────┤
│ Watchers (Actions on Change):                      │
│ • Watch(@Name) → Update UI                         │
│ • Watch(@Age) → Check if Adult                     │
│ • Watch(@Email) → Send Welcome Email               │
├────────────────────────────────────────────────────┤
│ Computed Properties (Auto-Calculated):             │
│ • @FullName ← @FirstName + " " + @LastName         │
│ • @IsAdult ← @Age >= 18                            │
└────────────────────────────────────────────────────┘

Attributes start with @ to show they're reactive. Watchers are like guards that react when something changes.

Basic Attribute Watching

Watching attributes is the starting point. It's like setting up a notification: "Tell me when this changes!"

Here's a simple example in Haro code. We'll create a reactive object for a user and watch their name and age.

Haro
load "../stzbase.ring"

# Create reactive system (like starting the engine)
Rs = new stzReactive()

# Create reactive object (our user)
oXUser = Rs.ReactiveObject()
oXUser.SetAttribute(:@Name, "")  # Start with empty name
oXUser.SetAttribute(:@Age, 0)    # Start with age 0

# Watch name changes (like a notification alert)
oXUser.Watch(:@Name, func(attr, oldval, newval) {
    ? "Name changed from (" + @@(oldval) + ") to (" + @@(newval) + ")"
})

# Watch age changes
oXUser.Watch(:@Age, func(attr, oldval, newval) {
    ? "Age changed from " + @@(oldval) + " to " + @@(newval)
})

# Test changes with delays (to simulate real-time)
Rs.SetTimeout(100, func {
    ? "Setting name to 'John'..."
    oXUser.SetAttribute(:@Name, "John")
    ? ""

    Rs.SetTimeout(500, func {
        ? "Setting age to 25..."
        oXUser.SetAttribute(:@Age, 25)
        ? ""

        Rs.SetTimeout(500, func {
            ? "Changing name to 'John Doe'..."
            oXUser.SetAttribute(:@Name, "John Doe")
        })
    })
})

Rs.Start()  # Start the reactive system
? NL + "✅ Sample completed."

illustration

Output:

Setting name to 'John'...
Name changed from ("") to ("John")

Setting age to 25...
Age changed from 0 to 25

Changing name to 'John Doe'...
Name changed from ("John") to ("John Doe")

✅ Sample completed.

Key Points:

  1. Anonymous Functions: The func is a small, unnamed function that runs when the attribute changes. It gets the attribute name, old value, and new value automatically.
  2. SetTimeout: This adds delays to mimic real-world timing, like user inputs happening over time.
  3. Attribute Naming: The @ prefix marks reactive attributes – it's a Softanza convention.
  4. Why This is Cool: No manual checks! The system watches for you.

Computed Attributes

Computed attributes are like smart formulas. They recalculate automatically when their "ingredients" (dependencies) change. Like in a spreadsheet: Change a number, and the sum updates.

Example: Full name from first and last name, or checking if someone is an adult based on age.

Haro
# Create reactive system
Rs = new stzReactive()

# Create user object with attributes
oXUser = Rs.ReactiveObject()
oXUser { 
    @(:@FirstName = "")    
    @(:@LastName = "")
    @(:@FullName = "")
    @(:@Email = "")
    @(:@Age = 0)
    @(:@IsAdult = false)
}

# Computed: FullName from FirstName + LastName
oXUser.Computed(:@FullName,
    func oSelf {
        return trim(oSelf.GetAttribute(:@FirstName) + " " + oSelf.GetAttribute(:@LastName))
    },
    [ :@FirstName, :@LastName ]
)

# Computed: IsAdult from Age
oXUser.Computed(:@IsAdult,
    func oSelf {
        return oSelf.GetAttribute(:@Age) >= 18
    },
    [ :@Age ]
)

# Watch computed changes
oXUser.Watch(:@FullName,
    func(oSelf, attr, oldval, newval) {
        ? "Full name computed: (" + newval + ")"
    }
)

oXUser.Watch(:@IsAdult,
    func(oSelf, attr, oldval, newval) {
        ? "Adult status: " + newval
    }
)

# Test with delays
Rs.SetTimeout(100, func {
    ? "Setting firstName to 'Jane'..."
    oXUser.SetAttribute(:@FirstName, "Jane")
    ? ""

    Rs.SetTimeout(500, func {
        ? "Setting lastName to 'Smith'..."
        oXUser.SetAttribute(:@LastName, "Smith")
        ? ""

        Rs.SetTimeout(500, func {
            ? "Setting age to 17..."
            oXUser.SetAttribute(:@Age, 17)
            ? ""

            Rs.SetTimeout(500, func {
                ? "Setting age to 21..."
                oXUser.SetAttribute(:@Age, 21)
            })
        })
    })
})

Rs.Start()
? NL + "✅ Sample completed."

illustration

Output:

Setting firstName to 'Jane'...
Full name computed: (Jane)

Setting lastName to 'Smith'...
Full name computed: (Jane Smith)

Setting age to 17...

Setting age to 21...
Adult status: 1

✅ Sample completed.

Computed Attributes Flow Diagram:

@FirstName = "Jane" ──┐
                      ├──→ @FullName = "Jane Smith" ──→ (Watcher: Print Update)
@LastName = "Smith" ──┘

@Age = 21 ──→ @IsAdult = true ──→ (Watcher: Print Status)

You define the formula once, and Softanza handles updates.

Attribute Binding

Binding links attributes between objects. Change one, and the others sync automatically – like mirroring data.

Example: A temperature sensor (source) updating multiple displays (targets).

Haro
# Create reactive system
Rs = new stzReactive()

# Source object (sensor)
oXSource = Rs.ReactiveObject()
oXSource {
    SetAttribute(:@Temperature, 20)
    SetAttribute(:@Status, "normal")
}

# Target objects (displays)
oXDisplay1 = Rs.ReactiveObject()
oXDisplay1 {
    SetAttribute(:@Temp, 0)
    SetAttribute(:@DisplayName, "Display1")
}

oXDisplay2 = Rs.ReactiveObject()
oXDisplay2 {
    SetAttribute(:@Temp, 0)
    SetAttribute(:@DisplayName, "Display2")
}

# Watch targets
oXDisplay1.Watch(:@Temp, func(attr, oldval, newval) {
    cDisplayName = oXDisplay1.GetAttribute(:@DisplayName)
    ? cDisplayName + " received temperature: " + newval + "°C"
})

oXDisplay2.Watch(:@Temp, func(attr, oldval, newval) {
    cDisplayName = oXDisplay2.GetAttribute(:@DisplayName)
    ? cDisplayName + " received temperature: " + newval + "°C"
})

# Bind source to targets
Rs.BindObjects(oXSource, :@Temperature, oXDisplay1, :@Temp)
Rs.BindObjects(oXSource, :@Temperature, oXDisplay2, :@Temp)

# Test updates
Rs.SetTimeout(100, func {
    ? "Setting source temperature to 25°C..."
    oXSource.SetAttribute(:@Temperature, 25)
    
    Rs.SetTimeout(500, func {
        ? "Setting source temperature to 30°C..."
        oXSource.SetAttribute(:@Temperature, 30)
        
        Rs.SetTimeout(500, func {
            ? "Setting source temperature to 35°C..."
            oXSource.SetAttribute(:@Temperature, 35)
        })
    })
})

Rs.Start()
? NL + "✅ Sample completed."

illustration

Output:

Setting source temperature to 25°C...
Display1 received temperature: 25°C
Display2 received temperature: 25°C

Setting source temperature to 30°C...
Display1 received temperature: 30°C
Display2 received temperature: 30°C

Setting source temperature to 35°C...
Display1 received temperature: 35°C
Display2 received temperature: 35°C

✅ Sample completed.

Binding Flow Diagram:

┌──────────────────────┐
│     Source Object    │
│   @Temperature = 30  │
└──────────┬───────────┘
           │
   Automatic Binding
           │
 ┌─────────▼─────────┐     ┌─────────────────┐
 │    Display1       │     │    Display2     │
 │    @Temp = 30     │     │    @Temp = 30   │
 └───────────────────┘     └─────────────────┘

Great for syncing data across parts of your app!

Batch Updates

Batch updates group changes together so watchers only trigger once at the end. This avoids unnecessary intermediate updates, like saving a form only after all fields are filled.

Example: Updating a product with multiple attributes.

Haro
# Create reactive system
Rs = new stzReactive()

# Create product object
oXProduct = Rs.ReactiveObject()
oXProduct.SetAttribute(:@Name, "")
oXProduct.SetAttribute(:@Price, 0)
oXProduct.SetAttribute(:@Category, "")
oXProduct.SetAttribute(:@InStock, false)

# Watch attributes
oXProduct.Watch(:@Name, func(attr, oldval, newval) {
    ? "  Name updated: " + newval
})

oXProduct.Watch(:@Price, func(attr, oldval, newval) {
    ? "  Price updated: $" + string(newval)
})

oXProduct.Watch(:@Category, func(attr, oldval, newval) {
    ? "  Category updated: " + newval
})

oXProduct.Watch(:@InStock, func(attr, oldval, newval) {
    ? "  Stock status: " + string(newval)
})

Rs.SetTimeout(100, func {
    ? "Individual updates (watch each change):"

    oXProduct.SetAttribute(:@Name, "Laptop")
    sleep(0.1)

    oXProduct.SetAttribute(:@Price, 999.99)
    sleep(0.1)

    oXProduct.SetAttribute(:@Category, "Electronics")
    sleep(0.1)

    oXProduct.SetAttribute(:@InStock, true)
    
    Rs.SetTimeout(1000, func {
        ? ""
        ? "Batch updates (all changes processed together):"

        oXProduct.Batch(func {
            oXProduct.SetAttribute(:@Name, "Gaming Laptop")
            oXProduct.SetAttribute(:@Price, 1299.99)
            oXProduct.SetAttribute(:@Category, "Gaming")
            oXProduct.SetAttribute(:@InStock, false)
        })
    })
})

Rs.Start()
? NL + "✅ Sample completed."

illustration

Output:

Individual updates (watch each change):
  Name updated: Laptop
  Price updated: $999.99
  Category updated: Electronics
  Stock status: 1

Batch updates (all changes processed together):
  Name updated: Gaming Laptop
  Price updated: $1299.99
  Category updated: Gaming
  Stock status: 0

✅ Sample completed.

Batch vs Individual Updates:

Individual Updates:                Batch Updates:
Change 1 ──→ Notify ──→ Update     Change 1 ┐
Change 2 ──→ Notify ──→ Update     Change 2 ├──→ Single Notify ──→ Update
Change 3 ──→ Notify ──→ Update     Change 3 ┘

Batching is efficient for grouped changes.

Attribute Streams

Streams treat attribute changes like a flowing river of data. You can transform, filter, or react to the flow.

Example: Processing sensor readings, alerting only for high values.

Haro
# Create reactive system
Rs = new stzReactive()

# Create sensor object
oXSensor = Rs.ReactiveObject()
oXSensor.SetAttribute(:@Value, 0)

# Stream from @Value changes
St = oXSensor.StreamAttribute(:@Value)

# Process the stream
St {
    Map(func(data) {
        # Get newValue from data
        newValue = data["newValue"]  # Simplified for clarity; use loop if needed
        return "Sensor reading: " + string(newValue)
    })

    Filter(func(message) {
        # Only >50
        reading = number(substr(message, len("Sensor reading: ") + 1))
        return reading > 50
    })

    OnData(func(message) {
        ? "🌡️ High reading alert: " + message
    })

    # Raw data watcher
    OnData(func(data) {
        ? "📊 Raw sensor data: " + string(data["newValue"])
    })
}

# Readings to test
anReadings = [10, 25, 60, 75, 30, 85, 45, 95]
nCurrentReading = 1

Rs.SetTimeout(100, func {
    NextReading()
})

Rs.Start()
? NL + "✅ Sample completed."

func NextReading()
    if nCurrentReading <= len(anReadings)
        value = anReadings[nCurrentReading]
        ? "Setting sensor value to: " + string(value)
        oXSensor.SetAttribute(:@Value, value)
        nCurrentReading++

        if nCurrentReading <= len(anReadings)
            Rs.SetTimeout(300, func { NextReading() })
        ok
    ok

illustration

Output:

Setting sensor value to: 10
📊 Raw sensor data: 10

Setting sensor value to: 25
📊 Raw sensor data: 25

Setting sensor value to: 60
📊 Raw sensor data: 60
🌡️ High reading alert: Sensor reading: 60

Setting sensor value to: 75
📊 Raw sensor data: 75
🌡️ High reading alert: Sensor reading: 75

Setting sensor value to: 30
📊 Raw sensor data: 30

Setting sensor value to: 85
📊 Raw sensor data: 85
🌡️ High reading alert: Sensor reading: 85

Setting sensor value to: 45
📊 Raw sensor data: 45

Setting sensor value to: 95
📊 Raw sensor data: 95
🌡️ High reading alert: Sensor reading: 95

✅ Sample completed.

Stream Processing Flow:

Raw Data ──→ Map (Transform) ──→ Filter (>50) ──→ Output
   10    ──→ "Reading: 10"  ──→     ❌       ──→ (none)
   60    ──→ "Reading: 60"  ──→     ✅       ──→ High Alert

Streams are powerful for handling ongoing data changes.

Debounced Attributes

Debouncing waits a bit before reacting, useful for rapid changes like typing in a search box. It prevents spamming actions (e.g., API calls) for every keystroke.

Example: Search query that only searches after typing stops.

Haro
# Create reactive system
Rs = new stzReactive()

# Create search object
oXSearch = Rs.ReactiveObject()
oXSearch.SetAttribute(:@Query, "")

# Immediate watch (fires on every change)
oXSearch.Watch(:@Query, func(attr, oldval, newval) {
    ? "🔍 Search query changed: " + @@(newval)
})

# Debounced (waits 800ms)
oXSearch.DebounceAttribute(:@Query, 800, func(attr, oldval, newval) {
    ? "🎯 Debounced search executed for: (" + newval + ")"
    ? "    (This simulates an API call)"
})

# Simulate typing
queries = ["h", "he", "hel", "hell", "hello", "hello w", "hello wo", "hello wor", "hello world"]
currentQuery = 1

Rs.SetTimeout(100, func {
    ? "Simulating rapid typing (debounced search will fire only after typing stops):"
    TypeNext()
})

Rs.Start()
? NL + "✅ Sample completed."

func TypeNext()
    if currentQuery <= len(queries)
        query = queries[currentQuery]
        ? "⌨️ Typing: " + @@(query)
        oXSearch.SetAttribute(:@Query, query)
        currentQuery++
        
        if currentQuery <= len(queries)
            Rs.SetTimeout(150, func { TypeNext() })
        else
            Rs.SetTimeout(1500, func { Rs.Stop() })
        ok
    ok

illustration

Output:

Simulating rapid typing (debounced search will fire only after typing stops):
⌨️ Typing: 'h'
🔍 Search query changed: 'h'
⌨️ Typing: 'he'
🔍 Search query changed: 'he'
⌨️ Typing: 'hel'
🔍 Search query changed: 'hel'
⌨️ Typing: 'hell'
🔍 Search query changed: 'hell'
⌨️ Typing: 'hello'
🔍 Search query changed: 'hello'
⌨️ Typing: 'hello w'
🔍 Search query changed: 'hello w'
⌨️ Typing: 'hello wo'
🔍 Search query changed: 'hello wo'
⌨️ Typing: 'hello wor'
🔍 Search query changed: 'hello wor'
⌨️ Typing: 'hello world'
🔍 Search query changed: 'hello world'
🎯 Debounced search executed for: (hello world)
    (This simulates an API call)

✅ Sample completed.

Debouncing Timeline:

Time:  0ms  150  300  450  600  750  900  1050  1200  ────→ 2000ms
Input: h    he   hel  hell hello w    wo   wor   world      (pause)
Watch: ✓    ✓    ✓    ✓    ✓    ✓    ✓    ✓     ✓          
Debounce:                                                ✓ (fires once after 800ms pause)

Perfect for optimizing performance in user inputs.

Making Existing Classes Reactive

You don't need to start from scratch! Softanza can make any regular Haro class reactive with ReactivateObject.

Example: Turning a simple Person class reactive.

Haro
# Regular class
class Person
    name = ""
    age = 0
    email = ""
    
    def init(pcName, pnAge)
        name = pcName
        age = pnAge

# Create instance
oPerson = new Person("Youssef", 28)

# Reactive system
Rs = new stzReactive()

# Make reactive
oXPerson = Rs.ReactivateObject(oPerson)

# Watch attributes
oXPerson.Watch(:name, func(attr, oldval, newval) {
    ? "✓ Name updated: " + oldval + " → " + newval
})

oXPerson.Watch(:age, func(attr, oldval, newval) {
    ? "✓ Age updated: " + oldval + " → " + newval
})

oXPerson.Watch(:email, func(attr, oldval, newval) {
    ? "✓ Email set: " + newval
})

# Test changes
Rs.SetTimeout(100, func {
    oXPerson {
        @(:name = "John Doe")
        @(:age = 26)
        @(:email = "john@test.com")
    }
    
    Rs.SetTimeout(500, func {
        ? ""
        ? "Current person info:"
        oXPerson {
            ? "  Name: " + name
            ? "  Age: " + age
            ? "  Email: " + email
        }
    })
})

Rs.Start()
? NL + "✅ Sample completed."

illustration

Output:

✓ Name updated: Youssef → John Doe
✓ Age updated: 28 → 26
✓ Email set: john@test.com

Current person info:
  Name: John Doe
  Age: 26
  Email: john@test.com

✅ Sample completed.

More Advanced Example - Reactive Bank Account:

Haro
# Regular class
class BankAccount
    balance = 0
    accountNumber = ""
    status = "active"
    
    def init(cNumber, nBalance)
        accountNumber = cNumber
        balance = nBalance

# Create instance
oAccount = new BankAccount("ACC-001", 1000)

# Reactive system
Rs = new stzReactive()

# Make reactive
oXAccount = Rs.ReactivateObject(oAccount)

# Watch with logic
oXAccount.Watch(:balance, func(attr, oldval, newval) {
    ? "💰 Balance: $" + oldval + " → $" + newval
    
    if newval < 100
        ? "⚠️ Low balance warning!"
    ok
    
    if newval > oldval
        ? "✅ Deposit detected: +" + (newval - oldval)
    else
        ? "📉 Withdrawal: -" + (oldval - newval)
    ok
})

oXAccount.Watch(:status, func(attr, oldval, newval) {
    ? "📄 Account status: " + oldval + " → " + newval
})

# Test
Rs.SetTimeout(100, func {
    ? "Processing deposit..."
    oXAccount.SetAttribute("balance", 1500)

    Rs.SetTimeout(500, func {
        ? ""
        ? "Processing withdrawal..."
        oXAccount.SetAttribute("balance", 50)

        Rs.SetTimeout(500, func {
            ? ""
            ? "Freezing account..."
            oXAccount.SetAttribute("status", "frozen")
        })
    })
})

Rs.Start()
? NL + "✅ Sample completed."

illustration

Output:

Processing deposit...
💰 Balance: $1000 → $1500
✅ Deposit detected: +500

Processing withdrawal...
💰 Balance: $1500 → $50
⚠️ Low balance warning!
📉 Withdrawal: -1450

Freezing account...
📄 Account status: active → frozen

✅ Sample completed.

This shows how to add reactivity to existing code without rewriting everything.

Framework Comparison

Here's how Softanza stacks up against popular JavaScript frameworks. Softanza is easier for Haro users and focuses on simplicity.

Feature Softanza Vue.js MobX RxJS Knockout
Language Haro JavaScript JavaScript JavaScript JavaScript
Learning Curve ⭐⭐ Easy ⭐⭐⭐ Moderate ⭐⭐⭐ Moderate ⭐⭐⭐⭐⭐ Hard ⭐⭐⭐ Moderate
Infrastructure libuv (Node.js) Custom Custom Custom Custom
Reactive Objects ✅ Native ✅ Native ✅ Native ❌ Manual ✅ Native
Computed Properties ✅ Yes ✅ Yes ✅ Yes ✅ Yes ✅ Yes
Data Binding ✅ Two-way ✅ Two-way ✅ Two-way ⚡ One-way ✅ Two-way
Debouncing ✅ Built-in ❌ Manual ❌ Manual ✅ Built-in ❌ Manual
Batch Updates ✅ Built-in ✅ Built-in ✅ Built-in ✅ Built-in ❌ Manual
Stream Processing ✅ Yes ❌ No ❌ No ✅ Advanced ❌ No
Object Conversion ✅ ReactivateObject ❌ No ✅ observable ❌ No ❌ No

Softanza shines in ease of use and built-in features for Haro developers.

Best Practices and Coding Style

To make your Softanza code clean and beginner-friendly:

  1. Use Clear Names: Prefix reactive attributes with @ for easy spotting.
  2. Keep Watchers Simple: One watcher per specific action – avoid cramming too much logic.
  3. Batch When Possible: Group related updates to improve performance.
  4. Test with Delays: Use SetTimeout to simulate real-world timing.
  5. Document Dependencies: In computed attributes, list all dependencies clearly.
  6. Start Small: Begin with basic watching, then add computed, bindings, etc.
  7. Error Handling: Add checks in watchers for invalid data.
  8. Reuse Objects: Reactivate existing classes to integrate with old code.

Follow these, and your code will be readable and maintainable!

Conclusion

Congratulations! You've journeyed through the basics of Softanza Reactive Objects, from simple watching to advanced streams and debouncing. Reactive programming with Softanza makes your code more efficient, responsive, and fun to write – especially in Haro.

Remember, the key is thinking in terms of "what happens when..." instead of manual steps. Start small: Try watching an attribute in your next project, then build up to computed properties and bindings.

Softanza's focus on simplicity means you can build powerful apps without the headache. Explore the other Softanza components like streams or timers in future tutorials. Happy coding, and may your data flow smoothly! 🚀

Citer

Mansour Ayouni. « Softanza Reactive Objects: A Complete Beginner's Tutorial ». Softanza narrations, 2025. https://mayouni.github.io/stzsite/fr/narrations/softanza-reactive-objects-narration.html

Non exécuté : son code touche aux fichiers, au réseau, à l'horloge ou au hasard. L'article est publié tel qu'il est écrit ; ses blocs sont des illustrations. Le code des articles est du code Haro. Là où un article a été écrit avant que la langue prenne son nom actuel, le nom a été changé et rien d'autre. Genre, domaine et série lus dans le nom du fichier, année tirée du premier commit : dérivés, jusqu'à ce que l'article porte son propre en-tête. Le fichier sur GitHub(s'ouvre dans un nouvel onglet)