91 | }
92 | ```
93 |
94 |
95 | ## Why Remini?
96 |
97 | Your coding time saver!
98 |
99 | Minimal, well structured, and flexible codebase save a lot of developer time for maintain and grouth your React applications.
100 |
101 | How it works
102 |
103 | Usually when you just start React project or have a very small one, your codebase is short, undestable and simple, you can easily google examples of common issues.
104 |
105 | But as you write the business logic of your application, the code gets larger and it becomes more and more difficult to understand the abundance of files, tricks and code pieces.
106 |
107 | You should clear understand where is a place to your logic, how you can write as many code as you want without reduce your application maintance.
108 |
109 | - How to make a simple React application who can easily upscale to large application by business demand
110 | - How to organize your code clean with minimal states and convinient separated logic
111 | - How to speed up your application and reduce boilerplate
112 |
113 | My answer is **Remini** 😍
114 |
115 |
116 | ## Multiple stores vs single store
117 |
118 | One of the manifestations is the **multiple-store** architecture. The main reason is the independent modules decomposition. For flexible growth, you should separate your code. Your app should be built on top of separated modules composition. There is each module contains some data and logic.
119 |
120 | It’s a very good architecture decision because you can develop and test each module separately. You can easily reuse modules between projects. And when you use a lazy load for some parts of your app, you will never have any problem with it, just import it and use it. It should be simple!
121 |
122 | Ok. The first one is the **separated module decomposition**, and what's the next?
123 |
124 | If each module has its own state and logic it is very convenient to use separate stores to control data flow.
125 |
126 | At that moment the good time to make the postulate: **each store should be simple**, and never recommend to make deeply nested state. The better way is following to KISS principle.
127 |
128 |
129 | ## Selection from store
130 |
131 | One of the most frequently used functions during work with the state is the selection. Selection is the transformation of your state, fairly for **performance reasons**. You should update your view components only when updated the data used inside. This is the **rendering optimization**.
132 |
133 | For example, your user state is big it has a lot of user settings and some stuff. If you have an avatar view component, it should be updated only when the avatar changes, not for each user state update.
134 |
135 | ```javascript
136 | import { box, get, wrap } from 'remini'
137 |
138 | const $user = box({
139 | name: 'Joe',
140 | email: 'a@x.com',
141 | settings: {},
142 | avatar: 'https://avatar.com/1.jpg'
143 | })
144 |
145 | const $avatar = wrap(() => get($user).avatar)
146 | ```
147 |
148 | ```javascript
149 | import { useBox } from 'remini/react'
150 |
151 | const Avatar = () => {
152 | const avatar = useBox($avatar)
153 | return (
154 |
155 | )
156 | }
157 | ```
158 |
159 | You can see how it’s easy to make that tiny, but very effective optimization!
160 |
161 | You don't have to render everything. You should render only what you need! No more, no less)
162 |
163 |
164 | ## Composition of stores
165 |
166 | Step by step on the application growing upstairs you will have cases of the necessary combination of multiple stores to one. It should be simple)
167 |
168 | ```javascript
169 | import { box, get, wrap } from 'remini'
170 |
171 | const $firstName = box('John')
172 | const $lastName = box('Doe')
173 |
174 | const $fullName = wrap(() => {
175 | return get($firstName) + ' ' + get($lastName)
176 | })
177 | ```
178 |
179 | Here we combine several stores into one for convenient use in some view components.
180 |
181 |
182 | ## References
183 |
184 | - [The dark mode switcher](./docs/dark-mode.md)
185 | - [Shared state](./docs/shared-state.md)
186 | - [Work together with Redux](./docs/redux.md)
187 | - [Pure reactivity in Node.js](./docs/nodejs.md)
188 |
189 |
190 | ## Install
191 |
192 | ```bash
193 | npm install remini
194 | # or
195 | yarn add remini
196 | ```
197 |
198 | Enjoy your code!
199 |
--------------------------------------------------------------------------------
/babel.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | ['@babel/preset-env', {targets: {node: 'current'}}],
4 | '@babel/preset-typescript',
5 | ],
6 | };
--------------------------------------------------------------------------------
/docs/dark-mode.md:
--------------------------------------------------------------------------------
1 | # The dark mode switcher
2 |
3 | A good example of a shared state benefit is the Dark mode switcher. Because you should get access to user choice in a big set of React components, it is very inconvenient to use props passing pattern.
4 |
5 | What is necessary to implement:
6 |
7 | - Provide convenient functions for changing user choices.
8 | - Provide access to user choice around the app code.
9 | - Keep user choice across browser sessions.
10 |
11 | Will clearly demonstrate how to create, use and propagate a shared state.
12 |
13 | Each shared state is stored in a special place created by calling the `box` function. This will be a reactive variable, which means we will be able to update all places where it is used when it changes.
14 |
15 | We will keep the dark mode enabled state in this way.
16 |
17 | To update the value of a reactive variable, we will use the `update` function. That takes the dark mode reactive variable as the first argument and the updater function as the second one. The updater function receives the current state in the first argument and returned the new state of dark mode.
18 |
19 | ```javascript
20 | // ./dark-mode.shared.js
21 | import { box, update } from 'remini'
22 |
23 | // create new reactive variable with "false" by default
24 | export const $darkMode = box(false)
25 |
26 | // create a function that should change dark mode to opposite each time calling
27 | export const toggleDarkMode = () => {
28 | update($darkMode, enabled => !enabled)
29 | }
30 | ```
31 |
32 | Now we can read and subscribe to dark mode changes everywhere we need.
33 |
34 | For easy binding to the React components, the `useBox` hook function is used. It allows you to get the value of the reactive variable, as well as automatically update the React component when the value changes.
35 |
36 | ```javascript
37 | import { useBox } from 'remini/react'
38 | import { $darkMode, toggleDarkMode } from './dark-mode.shared'
39 |
40 | export const DarkModeButton = () => {
41 | const darkMode = useBox($darkMode)
42 |
43 | return (
44 |
47 | )
48 | }
49 | ```
50 |
51 | Excellent! Now you can easily derive dark mode state to any React component using the same way. This is very simple, you should get state of the dark mode using the `useBox` hook, and it's all that you need. Each time when dark mode state will be changed, and all components using it will be updated automatically.
52 |
53 | And finally, we should make some code updates, because we almost forget to save user choice to browser local storage, to keep persistent between browser sessions.
54 |
55 | For accessing storage we will use the "localStorage" browser API. We will call "getItem" to retrieve the saved state, and call "setItem" to save it.
56 |
57 | ```javascript
58 | // import { set, on } from 'remini'
59 |
60 | // get choice from previous browser session
61 | set($darkMode, localStorage.getItem('darkMode') === 'on')
62 |
63 | // update user choice in browser local storage each time then it changed
64 | on($darkMode, enabled => {
65 | localStorage.setItem('darkMode', enabled ? 'on' : 'off')
66 | })
67 | ```
68 |
69 | The last operation in this example call of `on` function. It means that we subscribe to changes in dark mode reactive variable, and react on them each time changes for saving state to browser persistence storage.
70 |
71 | Brilliant! Now you can use it everywhere you want, it's worked well and should provide benefits for your users!
72 |
73 | [](https://codesandbox.io/s/darkmode-module-with-remini-5updlc?file=/src/App.js)
74 |
75 | It's looking good and provides you with convenient opportunities for controlling your shared state, and deriving in any parts of your application. You can create as many reactive variables as you want, it's quick and useful!
76 |
--------------------------------------------------------------------------------
/docs/nodejs.md:
--------------------------------------------------------------------------------
1 | # Pure reactivity in Node.js
2 |
3 | ```javascript
4 | import { box, get, set, update, wrap, on } from 'remini'
5 |
6 | const $value = box(0)
7 | const $next = wrap(() => get($value) + 1)
8 |
9 | on($next, n => console.log('Next value: ' + n))
10 |
11 | update($value, n => n + 1) // Next value: 2
12 | set($value, 2) // Next value: 3
13 |
14 | console.log(get($next)) // 3
15 | ```
16 |
17 | [](https://runkit.com/betula/62ac2287cdb97e00080fc9d5)
18 |
--------------------------------------------------------------------------------
/docs/redux.md:
--------------------------------------------------------------------------------
1 | # Work together with Redux
2 |
3 | It's easy! You can simply create Remini reactive variable from Redux store, and use it everywhere you want!
4 |
5 | ```javascript
6 | // ./remini-store.js
7 | import { box, set } from 'remini'
8 | import { store } from './redux-store'
9 |
10 | export const $store = box(store.getState())
11 |
12 | store.subscribe(() => {
13 | set($store, store.getState())
14 | })
15 | ```
16 |
17 | And you can make cached selectors for performance optimization reasons.
18 |
19 | ```javascript
20 | // ./remini-selectors.js
21 | import { get, wrap } from 'remini'
22 | import { $store } from './remini-store'
23 |
24 | export const $user = wrap(() => get($store).user)
25 |
26 | export const $fullName = wrap(
27 | () => `${get($user).firstName} ${get($user).lastName}`
28 | );
29 | ```
30 |
31 | And use it everywhere.
32 |
33 | ```javascript
34 | import { useBox } from 'remini/react'
35 | import { $fullName } from './remini-selectors'
36 |
37 | export const UserInfo = () => {
38 | const fullName = useBox($fullName)
39 |
40 | return
{fullName}
41 | }
42 | ```
43 |
44 | As you can see, everything is quite simple and can be effectively used together!
45 |
46 | [](https://codesandbox.io/s/redux-with-remini-ou9v4e?file=/src/components/UserInfo.js)
47 |
--------------------------------------------------------------------------------
/docs/shared-state.md:
--------------------------------------------------------------------------------
1 | # Shared state with Remini
2 |
3 | The key to winning is the shared state and logic. Pure React doesn't have a convenient way to organize shared states that can be used whole the application.
4 |
5 | Suggested React ways are passing state by props thought nesting components from parent to child, and using Context for giving shared access to some state values in children components. Both ways can't share state with any part of your app!
6 |
7 | Architecture with a shared state provides more simplest code. You can control your state and logic in separate files that can be accessed whole the app. You can easily change your shared state and read it everywhere.
8 |
9 | ## Simple counter demo
10 |
11 | ```javascript
12 | import { box, update } from 'remini';
13 | import { useBox } from 'remini/react';
14 |
15 | const $count = box(0)
16 | const inc = () => update($count, c => c + 1)
17 |
18 | const Counter = () => {
19 | const count = useBox($count)
20 | return
21 | {count}
22 |
23 |
;
24 | };
25 | ```
26 |
27 | ## Perfect frontend with modular architecture.
28 |
29 | - No need to wrap the application to Context Provider for each module.
30 | - Import and use, easy code for embedding.
31 |
32 | ## Modular counter demo
33 |
34 | ```javascript
35 | // ./counter.shared.js
36 | import { box, wrap, get, set } from 'remini'
37 |
38 | export const $count = box(0)
39 | export const $next = wrap(() => get($count) + 1)
40 |
41 | export const inc = () => update($count, n => n + 1)
42 | export const reset = () => set($count, 0)
43 | ```
44 |
45 | ```javascript
46 | import { get } from 'remini'
47 | import { component } from 'remini/react'
48 | import { $count, $next, inc, reset } from './counter.shared'
49 |
50 | const Counter = component(() => (
51 |