Skip to content

Commit 4905ff1

Browse files
authored
Merge pull request #10 from chadhietala/add-no-cp-action-docs
Add docs around no-cp-actions
2 parents a2d8fff + 4c2f80d commit 4905ff1

File tree

1 file changed

+54
-0
lines changed

1 file changed

+54
-0
lines changed

guides/rules/no-action-cp.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# No Computed Property Side-effects
2+
3+
In Ember we want data to flow down from the route and send actions up modifiy that data. This means that data flows from the top of the application tree structure and passes down to the child nodes. These children are not the owners of the data passed to them. Instead, they derive their own data based on the passed data. Those children can then choose to pass forward that derived data to its child and so on. If a child needs to mutate data passed to it, it must ask for it by firing any action. Once the owner makes the mutation, that change is propagated back down.
4+
5+
When we send actions in computed properties we are violating this pattern which makes systems much more difficult to reason about. Specifically with actions in computed propertites what we are semantically saying is:
6+
7+
1. When I first access this property send an action
8+
2. This action will not fire again unless the dependent key of this computed property changes.
9+
10+
The fact that property access could cause some side effect to occur is less than ideal as it forces you to write code that reacts to data changing over time instead of controlling the side effects based on user input into the system. Often when we see this pattern we see that one component is responsible displaying the data and another unrelated component is handles the action. This then forces a developer to look in mutliple places to understand how the component works. We can see this in the following example
11+
12+
```
13+
<input value={{firstName}} />
14+
{{if isValid 'VALID' 'INVALID'}}
15+
```
16+
17+
```
18+
Ember.Component.extend({
19+
init() {
20+
this._super(...arguments);
21+
this.firstName = null;
22+
},
23+
nameChanged: Ember.computed('firstName', function() {
24+
this.sendAction(this.get('validateAction'), this.get('firstName')); // Somebody else does the validation
25+
})
26+
})
27+
```
28+
29+
As you can see above, when we do side-effect programing we are forced to remove concerns of one component into another. More specically, `isValid` and the `validateAction` are passed to the component to call whenever the component changes it's `firstName` property. We can't look at just this component to see how validation is performed on our input. In contrast we can localize this logic and directly turn browser events e.g. oninput into semantic event `validateNameLength`. Here we are directly controlling the side-effects by taking an event in and directly handleing it, as opposed to rebroadcasting based on `firstName` changing.
30+
31+
```
32+
<input value={{firstName}} oninput={{action 'validateNameLength'}} />
33+
{{if isValid 'VALID' 'INVALID'}}
34+
```
35+
36+
```
37+
Ember.Component.extend({
38+
init() {
39+
this._super(...arguments);
40+
this.isValid = false;
41+
this.firstName = null;
42+
},
43+
actions: {
44+
validateNameLength() {
45+
if (this.firstName.length > 10) {
46+
this.set('isValid', true);
47+
} else {
48+
this.set('isValid', false);
49+
}
50+
}
51+
}
52+
})
53+
```
54+

0 commit comments

Comments
 (0)