Skip to content

Commit 5e6118d

Browse files
committed
Refactor action creators
1 parent 7386526 commit 5e6118d

File tree

10 files changed

+252
-160
lines changed

10 files changed

+252
-160
lines changed

README.MD

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ import createStoreContext from "react-concise-state";
1313
// 1️⃣ create a store context providing initial state and an actions
1414
const [context, Provider] = createStoreContext({
1515
counter: 0
16-
}, {
16+
}, ({ state, setState }) => ({
1717
// 👇 actions modify state using provided `setState`
18-
incrementBy: ({ state, setState }, increment: number) => {
18+
incrementBy: (increment: number) => {
1919
const newValue = state.counter + increment
2020
setState({ counter: newValue })
2121
},
22-
reset: ({ setState }) => setState({ counter: 0 })
23-
})
22+
reset: () => setState({ counter: 0 })
23+
}))
2424

2525
// 2️⃣ wrap component in created provider
2626
const App = props => {
@@ -153,6 +153,8 @@ Usually having plain state does not make any sense. There should be some way to
153153
* May or may not have a payload
154154
* May or may not return a value
155155
* May be async
156+
* May call own store actions
157+
* May be chained, injected, cached, curried etc.
156158
* May call other stores
157159
* May be written in functional & immutable approach or in imperative approach
158160

@@ -161,17 +163,17 @@ To create store action in `react-concise-state` provide a second argument to `cr
161163
Basic examples:
162164
```tsx
163165
// Imperative
164-
const actions = {
165-
someAction: ({state, setState}, payload) => {
166+
const actions = ({state, setState}) => ({
167+
someAction: ( payload) => {
166168
const newState = ... // do something
167169
setState(newState)
168170
}
169-
}
171+
})
170172

171173
// Functional
172-
const actions = {
173-
someAction: ({setState}, payload) => setState(prev => { ..prev, /* do something */})
174-
}
174+
const actions = ({setState}) => ({
175+
someAction: (payload) => setState(prev => { ..prev, /* do something */})
176+
})
175177

176178
// Create store
177179
createStoreContext(state, actions)
@@ -189,19 +191,19 @@ store.someAction('this is a payload string')
189191
Payload for actions is optional. There could be any amount of payload arguments.
190192

191193
```tsx
192-
const [context, Provider] = createStoreContext({counter: 0}, {
194+
const [context, Provider] = createStoreContext({counter: 0}, ({state, setState}) => ({
193195
// No payload
194-
increment: ({state, setState}) = setState({counter: ++state.counter}),
196+
increment: () => setState({counter: ++state.counter}),
195197
// If you are using functional style you can also get current state inside `setState` using callback function
196-
decrement: ({setState}) => setState(state => ({counter: --state.counter})),
198+
decrement: () => setState(state => ({counter: --state.counter})),
197199

198200
// With payload
199-
setValue: ({setState}, value) => setState({counter: value}),
200-
setValueIfMoreThan: ({state, setState}, value, limit) => {
201+
setValue: (value) => setState({counter: value}),
202+
setValueIfMoreThan: (value, limit) => {
201203
if(state.counter > limit)
202204
setState({counter: value})
203205
}
204-
})
206+
}))
205207

206208
...
207209

@@ -224,18 +226,18 @@ store.setValueIfMoreThan(9, 1)
224226
You can get return value from actions. It also enabled awaiting async actions.
225227

226228
```tsx
227-
const [context, Provider] = createStoreContext({todos: []}, {
229+
const [context, Provider] = createStoreContext({todos: []}, ({setState}) => ({
228230
// Returning a value
229-
addTodo: ({setState}, todo) => {
231+
addTodo: (todo) => {
230232
const result = Api.addTodo(todo)
231233
return result
232234
},
233235
// Getting todos asynchronously
234-
getTodos: async ({setState}) => {
236+
getTodos: async () => {
235237
const todos = await Api.getTodos()
236238
setState({todos})
237239
}
238-
})
240+
}))
239241

240242
...
241243

@@ -247,6 +249,30 @@ const result = store.addTodo('buy milk')
247249
await store.getTodos()
248250
```
249251

252+
You can call actions from other actions.
253+
254+
```tsx
255+
const [context, Provider] = createStoreContext({todos: []}, ({setState}) => ({
256+
addTodo(todo) {
257+
const result = Api.addTodo(todo)
258+
this.getTodos()
259+
},
260+
getTodos: async () => {
261+
const todos = await Api.getTodos()
262+
setState({todos})
263+
}
264+
}))
265+
266+
...
267+
268+
// Usage
269+
const store = React.useContext(context)
270+
271+
const result = store.addTodo('buy milk')
272+
273+
// await store.getTodos() - don't need to call it. `.addTodo` will call it
274+
```
275+
250276
</p></details>
251277

252278
#### Calling other stores
@@ -256,18 +282,18 @@ You can call actions in other stores by providing dependency contexts as a 3rd p
256282

257283
Example:
258284
```tsx
259-
const [todoContext, Provider] = createStoreContext({todos: []}, {
260-
addTodo: ({state, setState}, todo) => {
285+
const [todoContext, Provider] = createStoreContext({todos: []}, ({state, setState}) => ({
286+
addTodo: (todo) => {
261287
setState({todos: [...state.todos, todo]})
262288
},
263-
})
264-
const [mainContext, Provider] = createStoreContext({message: ''}, {
265-
someAction: ({setState, stores}, name) => {
289+
}))
290+
const [mainContext, Provider] = createStoreContext({message: ''}, ({setState, stores}) => ({
291+
someAction: (name) => {
266292
const { todos } = stores.todoContext // stores.todoContext is a "todo store" ({todos: [], addTodo: (todo) => void})
267293
const newMessage = `Hello, ${name}, you have ${todos.length} todos!`
268294
setState({message: newMessage})
269295
},
270-
}, {todoContext})
296+
}), {todoContext})
271297
...
272298
// Usage
273299
const todoStore = React.useContext(todoContext)

assets/action-types-provided.gif

-92 KB
Loading

docs/index.html

Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,14 @@ <h1 id="react-concise-state">react-concise-state</h1>
7575
// 1️⃣ create a store context providing initial state and an actions
7676
const [context, Provider] = createStoreContext({
7777
counter: 0
78-
}, {
78+
}, ({ state, setState }) =&gt; ({
7979
// 👇 actions modify state using provided `setState`
80-
incrementBy: ({ state, setState }, increment: number) =&gt; {
80+
incrementBy: (increment: number) =&gt; {
8181
const newValue = state.counter + increment
8282
setState({ counter: newValue })
8383
},
84-
reset: ({ setState }) =&gt; setState({ counter: 0 })
85-
})
84+
reset: () =&gt; setState({ counter: 0 })
85+
}))
8686

8787
// 2️⃣ wrap component in created provider
8888
const App = props =&gt; {
@@ -184,23 +184,25 @@ <h4 id="actions-modifying-state"><code>actions</code> - modifying state</h4>
184184
<li>May or may not have a payload</li>
185185
<li>May or may not return a value</li>
186186
<li>May be async</li>
187+
<li>May call own store actions</li>
188+
<li>May be chained, injected, cached, curried etc.</li>
187189
<li>May call other stores</li>
188190
<li>May be written in functional &amp; immutable approach or in imperative approach</li>
189191
</ul>
190192
<p>To create store action in <code>react-concise-state</code> provide a second argument to <code>createStoreContext</code> - an object where object keys are action names and values are actions themselves.</p>
191193
<p>Basic examples:</p>
192194
<pre><code class="language-tsx">// Imperative
193-
const actions = {
194-
someAction: ({state, setState}, payload) =&gt; {
195+
const actions = ({state, setState}) =&gt; ({
196+
someAction: ( payload) =&gt; {
195197
const newState = ... // do something
196198
setState(newState)
197199
}
198-
}
200+
})
199201

200202
// Functional
201-
const actions = {
202-
someAction: ({setState}, payload) =&gt; setState(prev =&gt; { ..prev, /* do something */})
203-
}
203+
const actions = ({setState}) =&gt; ({
204+
someAction: (payload) =&gt; setState(prev =&gt; { ..prev, /* do something */})
205+
})
204206

205207
// Create store
206208
createStoreContext(state, actions)</code></pre>
@@ -211,19 +213,19 @@ <h4 id="actions-modifying-state"><code>actions</code> - modifying state</h4>
211213
store.someAction(&#39;this is a payload string&#39;)</code></pre>
212214
<details><summary>Advanced (click to expand)</summary><p>
213215
<p>Payload for actions is optional. There could be any amount of payload arguments. </p>
214-
<pre><code class="language-tsx">const [context, Provider] = createStoreContext({counter: 0}, {
216+
<pre><code class="language-tsx">const [context, Provider] = createStoreContext({counter: 0}, ({state, setState}) =&gt; ({
215217
// No payload
216-
increment: ({state, setState}) = setState({counter: ++state.counter}),
218+
increment: () =&gt; setState({counter: ++state.counter}),
217219
// If you are using functional style you can also get current state inside `setState` using callback function
218-
decrement: ({setState}) =&gt; setState(state =&gt; ({counter: --state.counter})),
220+
decrement: () =&gt; setState(state =&gt; ({counter: --state.counter})),
219221

220222
// With payload
221-
setValue: ({setState}, value) =&gt; setState({counter: value}),
222-
setValueIfMoreThan: ({state, setState}, value, limit) =&gt; {
223+
setValue: (value) =&gt; setState({counter: value}),
224+
setValueIfMoreThan: (value, limit) =&gt; {
223225
if(state.counter &gt; limit)
224226
setState({counter: value})
225227
}
226-
})
228+
}))
227229

228230
...
229231

@@ -242,18 +244,18 @@ <h4 id="actions-modifying-state"><code>actions</code> - modifying state</h4>
242244
store.setValueIfMoreThan(9, 1)
243245
// &gt; store.counter is 1</code></pre>
244246
<p>You can get return value from actions. It also enabled awaiting async actions.</p>
245-
<pre><code class="language-tsx">const [context, Provider] = createStoreContext({todos: []}, {
247+
<pre><code class="language-tsx">const [context, Provider] = createStoreContext({todos: []}, ({setState}) =&gt; ({
246248
// Returning a value
247-
addTodo: ({setState}, todo) =&gt; {
249+
addTodo: (todo) =&gt; {
248250
const result = Api.addTodo(todo)
249251
return result
250252
},
251253
// Getting todos asynchronously
252-
getTodos: async ({setState}) =&gt; {
254+
getTodos: async () =&gt; {
253255
const todos = await Api.getTodos()
254256
setState({todos})
255257
}
256-
})
258+
}))
257259

258260
...
259261

@@ -263,23 +265,43 @@ <h4 id="actions-modifying-state"><code>actions</code> - modifying state</h4>
263265
const result = store.addTodo(&#39;buy milk&#39;)
264266

265267
await store.getTodos()</code></pre>
268+
<p>You can call actions from other actions.</p>
269+
<pre><code class="language-tsx">const [context, Provider] = createStoreContext({todos: []}, ({setState}) =&gt; ({
270+
addTodo(todo) {
271+
const result = Api.addTodo(todo)
272+
this.getTodos()
273+
},
274+
getTodos: async () =&gt; {
275+
const todos = await Api.getTodos()
276+
setState({todos})
277+
}
278+
}))
279+
280+
...
281+
282+
// Usage
283+
const store = React.useContext(context)
284+
285+
const result = store.addTodo(&#39;buy milk&#39;)
286+
287+
// await store.getTodos() - don&#39;t need to call it. `.addTodo` will call it</code></pre>
266288
</p></details>
267289
<h4 id="calling-other-stores">Calling other stores</h4>
268290
<p>Sometimes you would like to call other store action from an action. You can&#39;t use <code>React.useContext</code> because of specific hook rules in React. Hook amount should never change during runtime and only way to supply that is to initialize all dependency contexts before bootstraping actions.</p>
269291
<p>You can call actions in other stores by providing dependency contexts as a 3rd parameter to <code>createStoreContext</code>. Those contexts will be mapped to corresponding stores internally and will be available in <code>stores</code> object in <code>{setState, action, stores}</code> argument of action creator.</p>
270292
<p>Example:</p>
271-
<pre><code class="language-tsx">const [todoContext, Provider] = createStoreContext({todos: []}, {
272-
addTodo: ({state, setState}, todo) =&gt; {
293+
<pre><code class="language-tsx">const [todoContext, Provider] = createStoreContext({todos: []}, ({state, setState}) =&gt; ({
294+
addTodo: (todo) =&gt; {
273295
setState({todos: [...state.todos, todo]})
274296
},
275-
})
276-
const [mainContext, Provider] = createStoreContext({message: &#39;&#39;}, {
277-
someAction: ({setState, stores}, name) =&gt; {
297+
}))
298+
const [mainContext, Provider] = createStoreContext({message: &#39;&#39;}, ({setState, stores}) =&gt; ({
299+
someAction: (name) =&gt; {
278300
const { todos } = stores.todoContext // stores.todoContext is a &quot;todo store&quot; ({todos: [], addTodo: (todo) =&gt; void})
279301
const newMessage = `Hello, ${name}, you have ${todos.length} todos!`
280302
setState({message: newMessage})
281303
},
282-
}, {todoContext})
304+
}), {todoContext})
283305
...
284306
// Usage
285307
const todoStore = React.useContext(todoContext)

0 commit comments

Comments
 (0)