Redux: Is it wrong, this all-around action creator pattern? - javascript

I'm a newbie. I'm not sure, is using below the pattern wrong/silly?
import { createAction, handleActions } from "redux-actions";
const CHANGE_STATE = "appState/CHANGE_STATE";
export const changeState = createAction(CHANGE_STATE, (key, val) => ({ [key]: val }));
const initialState = {
maxBodySize: 1920,
isMaxBody: true,
isMobile: false
};
export default handleActions(
{
[CHANGE_STATE]: (state, { payload: changedState }) => {
const newState = {
...state,
...changedState
};
return newState;
}
},
initialState
);
Only one actionCreator editable every state. Like this:
// Can editable every state with 'changeState' action.
appState.changeState("isMaxBody", true);
appState.changeState("isMobile", true);
appState.changeState("maxBodySize", 960);
Can I continue to use this handy pattern?
If this pattern is bad, give some instructions to me, please.

Can I continue to use this handy pattern
I don't see why you "couldn't". It seems perfectly valid, in a strict sense.
As for whether or not it's a good idea, well, that's open to opinion, but I would not recommend using this pattern. Here's a few reasons:
You lose the abstraction between "actions" and "state modification". It's nice to be able to say "perform this action", and not have to worry about what the actual state changes are. Some actions may modify multiple parts of state. All of that is abstracted away from you, making it more controlled and testable. In a similar vein, this probably won't work well with, say, middleware, for similar reasons.
Modifying string properties "looks icky", and generally seems error prone. You'll get no help from tooling if you accidentally put a typo in a property name somewhere in your app.
Similar to the above, you lose any ability to leverage static typing (flow or TypeScript, for instance). Having written a decently large React/Redux app in a team, I highly recommend using something like TypeScript, and this pattern will not work well in a strongly/statically typed app.
Those are my opinions, derived from my experience. If the pattern works well for you and what you are trying to accomplish, you might find that it's just fine.
P.S. if you are going to use a pattern like this, I don't see why you wouldn't just do e.g. appState.changeState({isMaxBody: true}); instead of appState.changeState("isMaxBody", true);. It seems a little cleaner, and at least closer to allowing better typing.

Related

React saving array state using hooks

I'm trying to learn how to implement a list in react. The docs recommend that I use the hooks API to use state. I read through them, and I have an idea of how to implement one:
function ListReducer(list, action) {
switch(action.type) {
case 'push': {
listCopy = [...list]
listCopy.push(action.data);
return listCopy
}
}
}
const [state, dispatch] = useReducer(ListReducer, []);
However, this strikes me as very inefficient, since the entire list needs to be copied in order to return the value, and generally this only takes O(1). Is there a better way to handle this situation?
Since you're using React, there is no way to change the state of an array without iterating over the entire array - you can't, for example, just .push to the existing array, because that'd mutate the state.
There is no definitively better option than your general approach - but you can make the syntax look a bit nicer:
case 'push':
return [...list, action.data];
The efficiency of a given section of code rarely matters in real-world projects, usually. There are often one or a few sections of code that are bottlenecks, but the likelyhood that any given section you're looking at at the moment is the bottleneck is slim.
Don't worry about it until you see that your app is running slower than desirable, and when that happens, run a performance analysis to see what exactly the bottleneck is, and fix that - until then, it's often not worth taking the time to worry about.
that's the nature of using the state/reducer pattern, nothing out of the box in the JavaScript language to change/handle that...
there's a library called immer, the basic idea is that with Immer you will apply all your changes to a temporary draft, which is a proxy of the currentState. Once all your mutations are completed, Immer will produce the nextState based on the mutations to the draft state,
turning your code in:
import produce from "immer"
function ListReducer(list, action) {
switch(action.type) {
case 'push': {
let nextState = produce(list, draftState => {
draftState.push(action.data)
})
return nextState
}
}
}
const [state, dispatch] = useReducer(ListReducer, [])
when using immer, there's a significant reduction in the maintenance for your team on "are we using mutable/immutable APIs in JavaScript?"... with immer, you remove this problem

What is the right way for a reducer to affect state that is far away?

I am building a React-Redux project and I am trying to be idiomatic about my usage of Redux, and avoid hacking things together in a way that makes the code difficult to maintain later. I am also new to this ecosystem.
I have a nested state that looks something like this:
{ foo: {stuff}, bar: {baz: {stuff} } }
and I use combineReducers, so that foo and bar and baz all have their own reducers to interpret relevant actions for changing their own state. But I've run into a situation where an action could, depending on the state of baz, have an implication that might be of interest to foo.
I have basically three ideas, where I hate the first one and don't know how to do the other two:
1) Make the reducers for bar/baz have access to the whole state, and ask them to be responsible about it.
This makes this exact situation easy to deal with, but it seems bad from a separation of concerns perspective.
2) Somehow have the baz reducer dispatch a relevant action that foo would then pick up on.
This makes sense to me from a descriptiveness perspective, but I don't actually know how to do it. The fact that it's not obvious makes me think Redux is against this.
3) Import some magic library that makes this simple
I don't know what library would do this, though. It doesn't seem like this is what redux-thunk does (although I'm not sure) and then I really don't know.
I would suggest you keep your reducers simple. They are there to modify the slice of state they care for, and nothing else.
If an "action" affects more than one slice, then it should dispatch multiple, "actions".. The naming can get confusing at this point, but basically redux-thunk allows you to do just this.
If you have regular action creators, eg modifyFoo & modifyBar which simply return action objects which are dispatched to the reducers, with redux-thunk enabled you can create a more complex "action" which dispatches both. eg..
function modifyFoo(options) {
return { type: "MODIFY_FOO", payload: options }};
}
function modifyBar(options) {
return { type: "MODIFY_BAR", payload: options }};
}
function modifyFooBar(options) {
return (dispatch, getState) => {
const appState = getState();
dispatch(modifyFoo(options.foo, appState.baz));
dispatch(modifyBar(options.bar));
}
}
Using getState you have access to the full state of the app if you need it.
tl;dr -- I've decided to use redux-loop, which lets you describe side effects of a reducer without making the reducer pure/stateless.
Why I did not use redux-thunk for this:
The most common approach (based on my googling), represented in #lecstor's answer, is to use redux-thunk. In the typical redux-thunk code organization, you have to figure out everything that's going to happen first, then dispatch some numbers of actions, async operations, etc. through a thunk.
The downside in this case is the second action is (a) conditional on the state after the first action triggers, and (b) cannot be derived from the state itself, but must be derived from the fact that we have this state after this action.
So with redux-thunk you either have to duplicate the logic of the reducer (bad), or do all the business logic before the reducers, then launching a bunch of actions that describe state changes.
This is all to say, redux-thunk does its magic before the reducer happens.
Why I think redux-loop was a better fit:
On the other hand, redux-loop does magic after the reducer happens, allowing the reducer to describe those effects (including kicking off async actions, dispatching further actions, etc.) without breaking statelessness.
For the historical record, this means that in the foo reducer above you can do:
// ... nextState is the new fooState
if (isWeird(nextState)) {
return loop(nextState, Cmd.action(makeWeirdAction()));
} else {
return nextState;
}
so that all other reducers can interpret or ignore weird action as they please, instead of making these decisions all at once in the action creator.

simplify redux with generic action & reducer

In React-Redux project, people usually create multiple actions & reducers for each connected component. However, this creates a lot of code for simple data updates.
Is it a good practice to use a single generic action & reducer to encapsulate all data changes, in order to simplify and fasten app development.
What would be the disadvantages or performance loss using this method. Because I see no significant tradeoff, and it makes development much easier, and we can put all of them in a single file! Example of such architecture:
// Say we're in user.js, User page
// state
var initialState = {};
// generic action --> we only need to write ONE DISPATCHER
function setState(obj){
Store.dispatch({ type: 'SET_USER', data: obj });
}
// generic reducer --> we only need to write ONE ACTION REDUCER
function userReducer = function(state = initialState, action){
switch (action.type) {
case 'SET_USER': return { ...state, ...action.data };
default: return state;
}
};
// define component
var User = React.createClass({
render: function(){
// Here's the magic...
// We can just call the generic setState() to update any data.
// No need to create separate dispatchers and reducers,
// thus greatly simplifying and fasten app development.
return [
<div onClick={() => setState({ someField: 1 })}/>,
<div onClick={() => setState({ someOtherField: 2, randomField: 3 })}/>,
<div onClick={() => setState({ orJustAnything: [1,2,3] })}/>
]
}
});
// register component for data update
function mapStateToProps(state){
return { ...state.user };
}
export default connect(mapStateToProps)(User);
Edit
So the typical Redux architecture suggests creating:
Centralized files with all the actions
Centralized files with all the reducers
Question is, why a 2-step process? Here's another architectural suggestion:
Create 1 set of files containing all the setXField() that handle all the data changes. And other components simply use them to trigger changes. Easy. Example:
/** UserAPI.js
* Containing all methods for User.
* Other components can just call them.
*/
// state
var initialState = {};
// generic action
function setState(obj){
Store.dispatch({ type: 'SET_USER', data: obj });
}
// generic reducer
function userReducer = function(state = initialState, action){
switch (action.type) {
case 'SET_USER': return { ...state, ...action.data };
default: return state;
}
};
// API that we export
let UserAPI = {};
// set user name
UserAPI.setName = function(name){
$.post('/user/name', { name }, function({ ajaxSuccess }){
if (ajaxSuccess) setState({ name });
});
};
// set user picture URL
UserAPI.setPicture = function(url){
$.post('/user/picture', { url }, function({ ajaxSuccess }){
if (ajaxSuccess) setState({ url });
});
};
// logout, clear user
UserAPI.logout = function(){
$.post('/logout', {}, function(){
setState(initialState);
});
};
// Etc, you got the idea...
// Moreover, you can add a bunch of other User related methods,
// like some helper methods unrelated to Redux, or Ajax getters.
// Now you have everything related to User available in a single file!
// It becomes much easier to read through and understand.
// Finally, you can export a single UserAPI object, so other
// components only need to import it once.
export default UserAPI
Please read through the comments in the code section above.
Now instead of having a bunch of actions/dispatchers/reducers. You have 1 file encapsulating everything needed for the User concept. Why is it a bad practice? IMO, it makes programmer's life much easier, and other programmers can just read through the file from top to bottom to understand the business logic, they don't need to switch back and forth between action/reducer files. Heck, even redux-thunk isn't needed! And you can even test the functions one by one as well. So testability is not lost.
Firstly, instead of calling store.dispatch in your action creator, it should return an object (action) instead, which simplifies testing and enables server rendering.
const setState = (obj) => ({
type: 'SET_USER',
data: obj
})
onClick={() => this.props.setState(...)}
// bind the action creator to the dispatcher
connect(mapStateToProps, { setState })(User)
You should also use ES6 class instead of React.createClass.
Back to the topic, a more specialised action creator would be something like:
const setSomeField = value => ({
type: 'SET_SOME_FIELD',
value,
});
...
case 'SET_SOME_FIELD':
return { ...state, someField: action.value };
Advantages of this approach over your generic one
1. Higher reusability
If someField is set in multiple places, it's cleaner to call setSomeField(someValue) than setState({ someField: someValue })}.
2. Higher testability
You can easily test setSomeField to make sure it's correctly altering only the related state.
With the generic setState, you could test for setState({ someField: someValue })} too, but there's no direct guarantee that all your code will call it correctly.
Eg. someone in your team might make a typo and call setState({ someFeild: someValue })} instead.
Conclusion
The disadvantages are not exactly significant, so it's perfectly fine to use the generic action creator to reduce the number of specialised action creators if you believe it's worth the trade-off for your project.
EDIT
Regarding your suggestion to put reducers and actions in the same file: generally it's preferred to keep them in separate files for modularity; this is a general principle that is not unique to React.
You can however put related reducer and action files in the same folder, which might be better/worse depending on your project requirements. See this and this for some background.
You would also need to export userReducer for your root reducer, unless you are using multiple stores which is generally not recommended.
I mostly use redux to cache API responses mostly, here are few cases where i thought it is limited.
1) What if i'm calling different API's which has the same KEY but goes to a different Object?
2) How can I take care if the data is a stream from a socket ? Do i need to iterate the object to get the type(as the type will be in the header and response in the payload) or ask my backend resource to send it with a certain schema.
3) This also fails for api's if we are using some third party vendor where we have no control of the output we get.
It's always good to have control on what data going where.In apps which are very big something like a network monitoring application we might end up overwriting the data if we have same KEY and JavaScript being loosed typed may end this to a lot weird way this only works for few cases where we have complete control on the data which is very few some thing like this application.
Okay i'm just gonna write my own answer:
when using redux ask yourself these two questions:
Do I need access to the data across multiple components?
Are those components on a different node tree? What I mean is it isn't a child component.
If your answer is yes then use redux for these data as you can easily pass those data to your components via connect() API which in term makes them containers.
At times if you find yourself the need to pass data to a parent component, then you need to reconsider where your state lives. There is a thing called Lifting the State Up.
If your data only matters to your component, then you should only use setState to keep your scope tight. Example:
class MyComponent extends Component {
constructor() {
super()
this.state={ name: 'anonymous' }
}
render() {
const { name } = this.state
return (<div>
My name is { name }.
<button onClick={()=>this.setState({ name: 'John Doe' })}>show name</button>
</div>)
}
}
Also remember to maintain unidirectional data flow of data. Don't just connect a component to redux store if in the first place the data is already accessible by its parent component like this:
<ChildComponent yourdata={yourdata} />
If you need to change a parent's state from a child just pass the context of a function to the logic of your child component. Example:
In parent component
updateName(name) {
this.setState({ name })
}
render() {
return(<div><ChildComponent onChange={::this.updateName} /></div>)
}
In child component
<button onClick={()=>this.props.onChange('John Doe')}
Here is a good article about this.
Just practice and everything will start to make sense once you know how to properly abstract your app to separate concerns. On these matter composition vs ihhertitance and thinking in react are a very good read.
I started writing a package to make it easier and more generic. Also to improve performance. It's still in its early stages (38% coverage). Here's a little snippet (if you can use new ES6 features) however there is also alternatives.
import { create_store } from 'redux';
import { create_reducer, redup } from 'redux-decorator';
class State {
#redup("Todos", "AddTodo", [])
addTodo(state, action) {
return [...state, { id: 2 }];
}
#redup("Todos", "RemoveTodo", [])
removeTodo(state, action) {
console.log("running remove todo");
const copy = [...state];
copy.splice(action.index, 1);
return copy;
}
}
const store = createStore(create_reducer(new State()));
You can also even nest your state:
class Note{
#redup("Notes","AddNote",[])
addNote(state,action){
//Code to add a note
}
}
class State{
aConstant = 1
#redup("Todos","AddTodo",[])
addTodo(state,action){
//Code to add a todo
}
note = new Note();
}
// create store...
//Adds a note
store.dispatch({
type:'AddNote'
})
//Log notes
console.log(store.getState().note.Notes)
Lots of documentation available on NPM. As always, feel free to contribute!
A key decision to be made when designing React/Redux programs is where to put business logic (it has to go somewhere!).
It could go in the React components, in the action creators, in the reducers, or a combination of those. Whether the generic action/reducer combination is sensible depends on where the business logic goes.
If the React components do the majority of the business logic, then the action creators and reducers can be very lightweight, and could be put into a single file as you suggest, without any problems, except making the React components more complex.
The reason that most React/Redux projects seem to have a lot of files for action creators and reducers because some of the business logic is put in there, and so would result in a very bloated file, if the generic method was used.
Personally, I prefer to have very simple reducers and simple components, and have a large number of actions to abstract away complexity like requesting data from a web service into the action creators, but the "right" way depends on the project at hand.
A quick note: As mentioned in https://stackoverflow.com/a/50646935, the object should be returned from setState. This is because some asynchronous processing may need to happen before store.dispatch is called.
An example of reducing boilerplate is below. Here, a generic reducer is used, which reduces code needed, but is only possible the logic is handled elsewhere so that actions are made as simple as possible.
import ActionType from "../actionsEnum.jsx";
const reducer = (state = {
// Initial state ...
}, action) => {
var actionsAllowed = Object.keys(ActionType).map(key => {
return ActionType[key];
});
if (actionsAllowed.includes(action.type) && action.type !== ActionType.NOP) {
return makeNewState(state, action.state);
} else {
return state;
}
}
const makeNewState = (oldState, partialState) => {
var newState = Object.assign({}, oldState);
const values = Object.values(partialState);
Object.keys(partialState).forEach((key, ind) => {
newState[key] = values[ind];
});
return newState;
};
export default reducer;
tldr It is a design decision to be made early on in development because it affects how a large portion of the program is structured.
Performance wise not much. But from a design perspective quite a few. By having multiple reducers you can have separation of concerns - each module only concerned with themselves. By having action creators you add a layer of indirection -allowing you to make changes more easily. In the end it still depends, if you don't need these features a generic solution helps reduce code.
First of all, some terminology:
action: a message that we want to dispatch to all reducers. It can be anything. Usually it's a simple Javascript object like const someAction = {type: 'SOME_ACTION', payload: [1, 2, 3]}
action type: a constant used by the action creators to build an action, and by the reducers to understand which action they have just received. You use them to avoid typing 'SOME_ACTION' both in the action creators and in the reducers. You define an action type like const SOME_ACTION = 'SOME_ACTION' so you can import it in the action creators and in the reducers.
action creator: a function that creates an action and dispatches it to the reducers.
reducer: a function that receives all actions dispatched to the store, and it's responsible for updating the state for that redux store (you might have multiple stores if your application is complex).
Now, to the question.
I think that a generic action creator is not a great idea.
Your application might need to use the following action creators:
fetchData()
fetchUser(id)
fetchCity(lat, lon)
Implementing the logic of dealing with a different number of arguments in a single action creator doesn't sound right to me.
I think it's much better to have many small functions because they have different responsibilities. For instance, fetchUser should not have anything to do with fetchCity.
I start out by creating a module for all of my action types and action creators. If my application grows, I might separate the action creators into different modules (e.g. actions/user.js, actions/cities.js), but I think that having separate module/s for action types is a bit overkill.
As for the reducers, I think that a single reducer is a viable option if you don't have to deal with too many actions.
A reducer receives all the actions dispatched by the action creators. Then, by looking at the action.type, it creates a new state of the store. Since you have to deal with all the incoming actions anyway, I find it nice to have all the logic in one place. This of course starts to be difficult if your application grows (e.g. a switch/case to handle 20 different actions is not very maintainable).
You can start with a single reducer, the move to several reducers and combine them in a root reducer with the combineReducer function.

Reimplement redux observable with only RxJs?

I am trying to see (out of curiosity) how complex it would be to reimplement basic redux / redux-observable behavior with pure Rxjs.
Here is my take on it, but it seems incredibly too simple to be right. Can anyone point me at any errors/flaws in my logic ?
Thank you very much
// set up the store.dispatch functionnaly through a subject (action$.next() is like store.dispatch())
var action$ = new Rx.Subject()
// Create epics that do nothing interesting
function epic1(action$) {
return action$.filter(action => action.type == "test").delay(1000).mapTo({
type: "PONG"
})
}
function epic2(action$) {
return action$.filter(action => action.type == "test2").delay(2000).mapTo({
type: "PING"
})
}
//....
//Later on, Merge all epic into one observable
//
function activateAndMergeEpics(action$, ...epics) {
// give the action$ stream to each epic
var activatedArray = epics.map(epic => epic(action$))
// merge them all into one megaObservable
var merged = Rx.Observable.merge(...activatedArray)
return merged
}
var merged = activateAndMergeEpics(action$, epic1, epic2)
// Pipe your megaObservable back inside the loop so
// you can process the action in your reducers
var subscription = merged.subscribe(action$)
function rootReducer(state = {}, action) {
console.log(action)
return (state)
}
// Generate your state from your actions
var state$ = action$.scan(rootReducer, {})
// Do whatever your want now, like...
// state$.map(route).map(renderdom)
// Let's juste subscribe to nothing to get the stream pumping
state$.subscribe()
// Simulate a dispatch
action$.next({
type: "test"
})
// Another one
action$.next({type:"test2"})
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.4.3/Rx.min.js"></script>
Yep, you've totally got the core functionality.
I hope you don't mind some unsolicited advice: If you're doing this just to learn how it works, I applaud you! That's such a great and surprisingly rare trait, even among programmers. I do want to caution against using your own home rolled redux clone because then you lose a lot of the huge benefits of redux; devtools, middleware, enhancers. You lose all of its built-in assertions/error checking, which actually is most of the code in redux (some of which is stripped out in production builds). You also loose fixes for edge cases that get shaked out over the years, which is sometimes why a given library might appear unnecessarily complex to anyone without that context.
You could add all those things, but then it would just be redux 🙃
If you do decide to go down that route, checkout some of the existing RxJS-based clones for inspiration (or collaboration) on yours:
https://www.npmjs.com/package/reactive-state
https://www.npmjs.com/package/rxdux
https://www.npmjs.com/package/oddstream
https://www.npmjs.com/package/rstore

Redux - Making Impossible States Impossible

TL;DR
Is there a library which makes something like Elm-Union Types possible in JavaScript with Redux and ImmutableJS?
Idea
Hi!
I am rather new to Redux, still exploring best patterns to use. I spent some time learning Elm and in Elm the core focus of whole application is the Model.
The core prinicple of Elm is making errorless applications. In comparison with JavaScript, Elm is packed with basic types (String, List, Boolean...) as it's also a static typed language. On the other hand you can use ImmutableJS and Flow and get pretty decent amount of full types and type check also in JavaScript, which makes this very even.
But the contrast between Elm and JavaScript comes with a feature called Union Types. With Union Types you can say something like:
type alias User = {
name: String
}
type Session = LoggedOut | LoggedIn User
What this means is that we have two options when it comes to session:
There is no session and we have no information about the user.
User is authenticated and we know who the user is.
This is extremely useful when dealing with "HOC" as we can say simply:
case session of
LoggedOut ->
displayLoginButton
LoggedIn user ->
displayUser user
Furthermore you can use the same pattern when fetching data. When data is fetching the state is Fetching once it is fetched, the state is Fetched Data and in case of an error, the state is FetchingError Error.
This makes data very secure and very clean. The same problem written in JavaScript/Redux would normaly be tackled something like this:
const model = {
loading: false,
loaded: false,
error: null,
data: null
}
At first this might seem normal, but as you think you see that "impossible" situations can be encountered. For example:
const model = {
loading: true,
loaded: true,
error: null,
data: null
}
The data can't be loading and be loaded at the same time!
Question
Is there a way to acheive similar concept with JavaScript-Redux and still remain the immutability of ImmutableJS?
Or if there's a way to rearrange the model to make impossible states impossible in Redux?
P.S.
Great talk about this in Elm: "Making Impossible States Impossible" by Richard Feldman
No, there's nothing specifically built into React or Redux that enables that. That sort of thing is really more at the language level. You might want to look into TypeScript or Flow to add static types that help with that. Beyond that, you can probably put something together using an explicit state machine approach or careful reducer logic checks.
My React/Redux links list has a section on static typing tools that may be useful in getting started. In addition, since you mentioned the "loading states" example, the React and Ajax category in my list specifically links to several articles that discuss ways to handle that concept in TS, Flow, and a couple other solutions.
You can hack it like this:
const Session = {
LOGGED_IN: user => ({ type: 'LOGGED_IN', user }),
LOGGED_OUT: () => ({ type: 'LOGGED_OUT' })
}
switch (session.type) {
case 'LOGGED_IN': {
viewUser(session.user)
}
case 'LOGGED_OUT': {
viewLoginButton()
}
}

Categories

Resources