Haven’t you been bothered by the sheer amount of work you’d have to put into a project when you use Redux as your state management tool?
Want to use Relessjs immediately? Go to npmjs.com/package/relessjs
Well I have! Especially when the project grows. Then I want to keep things DRY and put my Action Types in one place, because I need to reference them everywhere. Yes, I know, you can still put strings { type: “FILTER" }
in there, but then I’ve got to match that to my reducers so that’s already wet (not-DRY).
It’s especially annoying when I’d have to create action factories to do some async stuff. Calling the factory, referencing the action-types, using the type’s in the created actions and, don’t forget it, reducing them.
Implementing search in Redux
Let’s say I have a ToDo app and I want todo’s to be filtered based on text. A simple search feature. To implement this I’d have to do the following:
- add the action type
var actions = { filter: “FILTER” }
to todo-actions.js (this is where I put all my constants, to make sure I keep it DRY) - import todo-actions.js into the todo-reducers.js
- create a new case for
actions.filter
in thereducer
- import todo-actions.js into search-component.js
- implement the
dispatch({ type: actions.filter, payload: event.target.value })
in order to get the loop going
Yes, too… much… work… We can do better:
- remove step 1 (no need for an action type);
- remove step 2 (no action-types means: no constants);
- remove step 4 (idem); and
- instead of step 5 (calling
dispatch()
), we would just call the reducerreducers.search(payload)
and - implement step 3 in the reducer
Implementing search in Reless
I’ve been building Relessjs. A package to provide the same core concepts of the Flux architecture as Redux does but with less overhead.
So now we just call a Reducer from the View and we get a new State on which the View will be rerendered.
Relessjs: unidirectional dataflow without overhead
Now my new search feature for the ToDo app is simplified to only 2 steps:
- Create the reducer named
search(payload)
to the list of reducers from Reless - Call the
reducers.search(input.value)
from the new search component
Awesome right! Just create and call.
Simplicity: create a reducer, call a reducer
The power of Reless
Below are a few examples of the power that Reless has to offer.
Setting a value, to simply set the state, the reducer is of the following type payload => state
: a function that gets a payload
and returns a new partial state
. Partial state, because it’s being merged with the existing state. This removes the need for an Object.assign({}, value)
or { ...state }
on the root level.
Incrementing a counter, updating state based on another state:
As shown above (setting a value), you don’t get the state as a second parameter in the first function. To use the state Reless allows you to return a function from the reducer of type state => state
. This way you can use the state
passed in the second function.
var store = new Reless({
state: { counter: 0 },
reducers: {
increment: payload => state => ({ counter: state.counter + 1 })
}
})
Fetching data, doing some async stuff (and using partial state):
Well, now you know the drill. You can return another function of type reducers => state
where you can call another reducer
in the same manner as mentioned before and/or return a state
that goes effective immediately after finishing the function.
var store = new Reless({
state: { todos: [] },
reducers: {
getAllTodos: payload => state => reducers => {
fetch('some.url/api/todos')
.then((todos) => reducers.setTodos(todos))
return ({ loading: true })
},
// since Reless does a merge we can return
// a partial state instead of a full state
setTodos: todos => ({ todos })
})
Counting down a timer, doing something continuously based on the latest state:
For this we need to do a little trick. Instead of calling a reducer with a payload
we call it with another function that returns a payload state => payload
. This way we get access to the latest state
.
(In the particular case below, you can also just call decrement
, but in other cases you might want to call the reducer
(setCounter
) with a payload)
var store = new Reless({
state: { counter: 20 },
reducers: {
decrement: () => state => ({ state.counter: state.counter-1 }),
setCounter: payload => ({ counter: payload }),
getAllTodos: payload => state => reducers => {
var countdownHandle = setInterval(() => {
if (state.counter)
reducers.setCounter(state => state.counter - 1)
else
clearInterval(countdownHandle)
}, 1000)
}
})
Don’t forget to clap if you liked this. However, if you think something is badly worded, please give me some feedback. I do appreciate it.