|
1 | 1 | # Editors and Widgets
|
2 | 2 |
|
3 | 3 | ## Editor
|
4 |
| -A file editor uses `register` from `project_file.coffee`. |
| 4 | + |
| 5 | +A file editor uses `register`. |
5 | 6 | A family of file editors is the set of editors which use the same store.
|
6 |
| -Each family of editors has its own folder inside `smc-webapp/`. |
7 |
| -Inside this folder is typically the following |
8 |
| -- main.cjsx |
9 |
| -- register.coffee |
10 |
| -- actions.coffee |
11 |
| -- store.coffee |
12 |
| -- README.md |
13 |
| - |
14 |
| -Additional supporting files are also common: |
15 |
| -- info.coffee |
16 |
| -- styles.coffee |
17 |
| -- util.coffee |
| 7 | +Each family of editors has its own folder inside `packages/frontend/`. |
18 | 8 |
|
19 | 9 | Treat these folders somewhat like modules.
|
20 |
| -Typically you will only be importing from `main.cjsx` and `register.coffee`. |
21 |
| - |
22 |
| -## Widget |
23 |
| -Widgets stand alone and do not call `register`. |
24 |
| -See "smc-webapp/widget-markdown-input/" for an example. |
25 | 10 |
|
26 | 11 | ## Actions, Tables, Stores, Oh My!
|
| 12 | + |
27 | 13 | We use
|
28 | 14 | CQRS = command query responsibility segregation.
|
29 | 15 |
|
30 |
| -It means that you have two different things you communicate with (e.g., "Actions" and "Store"). One is used only for commands (=actions). The other is used only to get out data (=store). Segregation means they are completely separate. It's the design pattern we're using. |
| 16 | +It means that you have two different things you communicate with \(e.g., "Actions" and "Store"\). One is used only for commands \(=actions\). The other is used only to get out data \(=store\). Segregation means they are separate. It's the design pattern we're using. |
31 | 17 |
|
32 | 18 | If something doesn't fit into that at all, and you don't want to change the store or UI to reflect that, then that something should be somewhere else -- not in actions or store.
|
33 |
| -CQRS = command query responsibility segregation. |
34 | 19 |
|
35 | 20 | ### Stores
|
| 21 | + |
36 | 22 | - Store functions should always be pure functions
|
37 | 23 |
|
38 |
| -Computed values are |
39 |
| - - Only get called when a component is rendered which depends on the function |
40 |
| - - Callable from outside React code |
41 |
| - - Not stored in the redux state. Maintain redux state as pure data |
42 |
| - |
43 |
| -```ts |
44 |
| -{redux, computed, depends, rtypes} = require('app-state-framework') |
45 |
| -redux.createStore |
46 |
| - name: 'account' |
47 |
| - |
48 |
| - # state types without a defined selector default to |
49 |
| - # @get('key_name') from the store |
50 |
| - # Prefer to write store.get('key_name') to be explict |
51 |
| - stateTypes : |
52 |
| - time : rtypes.string |
53 |
| - user_type : rtypes.string |
54 |
| - first_name : rtypes.string |
55 |
| - last_name : rtypes.string |
56 |
| - full_name : computed rtypes.string |
57 |
| - greetings : computed rtypes.string |
58 |
| - |
59 |
| - # Define the dependencies of computed values |
60 |
| - full_name: depends('first_name', 'last_name') -> |
61 |
| - return "#{@first_name} + #{@last_name}" |
62 |
| - |
63 |
| - # Computed values may call other computed values |
64 |
| - greeting: depends('full_name', 'time') -> |
65 |
| - return "Hello #{@full_name} good #{get_english_time_period_name(@time)}" |
66 |
| - |
67 |
| -# Define private functions outside of the store declaration |
68 |
| -get_english_time_period_name: (time_of_day) -> |
69 |
| - # ... some computation |
70 |
| - |
71 |
| -``` |
72 |
| -Importing them in a high level component: |
73 |
| -```coffee |
74 |
| -ProjectPage = rclass |
75 |
| - reduxProps: |
76 |
| - account : |
77 |
| - full_name : rtypes.string |
78 |
| - greeting : rtypes.string |
79 |
| - |
80 |
| -``` |
81 |
| -Importing values from outside stores |
82 |
| -```coffee |
83 |
| -redux.createStore |
84 |
| - name: "project_store" |
85 |
| - |
86 |
| - # Declares what values to import from other stores |
87 |
| - # These are not available using reduxProps but are |
88 |
| - # available as dependencies for computed values |
89 |
| - reduxState: |
90 |
| - account : |
91 |
| - full_name : rtypes.string |
92 |
| - |
93 |
| - stateTypes: |
94 |
| - shown_greeting : computed rtypes.string |
95 |
| - |
96 |
| - shown_greeting: depends('full_name') -> |
97 |
| - return "Hello, " + @full_name |
98 |
| -``` |
99 |
| -Run time store definitions can be created as follows: |
100 |
| -```coffee |
101 |
| -create_project_store_def = (name, project_id) -> |
102 |
| - name: name |
103 |
| - |
104 |
| - project_id: project_id |
105 |
| -``` |
| 24 | +Computed values are used in a few places. They were a bad idea and all code that uses them should be rewritten. React hooks solve the same problem in a much better way. |
106 | 25 |
|
107 | 26 | ### Actions
|
108 |
| -Actions are called to make state changes. |
109 |
| -They do not directly manipulate the UI. |
110 | 27 |
|
| 28 | +Actions are called to make state changes. They do not directly manipulate the UI. |
111 | 29 |
|
112 | 30 | ## Q and A
|
113 | 31 |
|
114 |
| -* [hsy]> How to change a value in a store? What patterns are preferred? |
115 |
| - |
116 |
| -* [hsy]> What are the steps to make a react component actually "react" to changes in a given store? |
117 |
| - * preparation step: |
118 |
| - * setup step: |
119 |
| - * details to take care of? (e.g. control exactly when to re-render) |
120 | 32 |
|
121 | 33 | The following questions are specific for projects, but are meant to be general:
|
122 | 34 |
|
123 |
| -* not project related, maybe callback, maybe return value, doesn't change store → misc |
| 35 | +- not project related, maybe callback, maybe return value, doesn't change store → misc |
124 | 36 |
|
125 |
| -It depends. misc is pure javascript and generally just stuff that could be used on both the frontend and backend and doesn't have anything to do with sage_client communication. It's utility functions. |
| 37 | +It depends. misc is pure javascript and generally just stuff that could be used on both the frontend and backend and doesn't have anything to do with sage_client communication. It's utility functions. |
126 | 38 |
|
127 |
| -* project related, maybe callback, no return value, doesn't change store → store ? (e.g. `ensure_directory_exists` ?) |
| 39 | +- project related, maybe callback, no return value, doesn't change store → store ? (e.g. `ensure_directory_exists` ?) |
128 | 40 |
|
129 |
| -No. The methods of the store should all be "pure" functions of the immutable js state. There should be no callbacks ever, and nothing that should have any impact on any state anywhere. The store is a container of state and the interface is "ways to get at that state". (Exception: there is a method called "wait", which calls a callback when a certain condition on the store holds.) |
| 41 | +No. The methods of the store should all be "pure" functions of the immutable js state. There should be no callbacks ever, and nothing that should have any impact on any state anywhere. The store is a container of state and the interface is "ways to get at that state". (Exception: there is a method called "wait", which calls a callback when a certain condition on the store holds.) |
130 | 42 |
|
131 |
| -* project related, maybe callback, has return value, doesn't change store → somewhere else ? |
| 43 | +- project related, maybe callback, has return value, doesn't change store → somewhere else ? |
132 | 44 |
|
133 |
| -Somewhere else, e.g,. a module scope function or class or function in client.coffee. We want to minimize these as much as possible, as they are harder to reason about, but obviously sometimes they are necessary. Example: synctables involve tons of such methods. |
| 45 | +Somewhere else, e.g,. a module scope function or class or function in client.coffee. We want to minimize these as much as possible, as they are harder to reason about, but obviously sometimes they are necessary. Example: synctables involve tons of such methods. |
134 | 46 |
|
135 |
| -* project related, no callback, no return value, changes store → action |
| 47 | +- project related, no callback, no return value, changes store → action |
136 | 48 |
|
137 |
| -Yes, that's the ideal case. These can of course be asynchronous functions -- e.g., copying a file -- but rather than expressing what happens as it progresses via callback(s), the instead update the store as the run. Then the UI can display what happens (or not). |
| 49 | +Yes, that's the ideal case. These can of course be asynchronous functions -- e.g., copying a file -- but rather than expressing what happens as it progresses via callback(s), the instead update the store as the run. Then the UI can display what happens (or not). |
138 | 50 |
|
139 |
| -* project related, has callback, no return value, maybe changes store → action, but only for "internal" methods |
| 51 | +- project related, has callback, no return value, maybe changes store → action, but only for "internal" methods |
140 | 52 |
|
141 | 53 | Yes, to write clean code the non-public api for Actions can have all kinds of such "traditional" methods.
|
142 | 54 |
|
143 |
| -* project related, no callback, has return value, changes store → shouldn't exist at all |
| 55 | +- project related, no callback, has return value, changes store → shouldn't exist at all |
144 | 56 |
|
145 | 57 | Yes, exactly.
|
146 | 58 |
|
|
0 commit comments