Best way to build communication between redux libraries - javascript

We are trying to split our project on to pieces:
We have libraries e.g user-library and image-library.
Each library has their own reducer, and actions e.g:
for user-library:
{isloggein: false} // store
function logIn() { //action creator
return {
type: 'LOGIN'
};
}
for image-library:
{images: []} // store
function addImages() { //action creator
return {
type: 'ADDIMAGES'
};
}
Now we decide that we want addImages as soon as user logIn. Problem is image store now nothing about user actions (that is main idea of code splitting). We solved that problem by using sagas like this:
function* addImagesWhenLogIn() {
while (true) {
yield take('LOGIN');
yield put(addImages());
}
}
But it is not opaque (we dispatched one action but 2 actions will be dispatch actually). While it is fine to have business logic of application in sagas, I believe that take -> put sequence is not the best solution in such case, and I am looking for better way of doing it.

Some of the advantages of redux architecture is the capability to easy hydration, snapshots, time travel, and it works best if there is a single place where state is stored.
From redux docs:
Single source of truth - The state of your whole application is stored
in an object tree within a single store. Ref.
Therefore a unique state for your redux app is recommended.
In your case I would suggest:
You could keep separated your libraries/modules, import them as you need in your app.
Use only a single state tree as suggested in redux docs.
Provide state information to your representational components using containers.

I think that's a perfectly valid approach. Your two main options are to either have one module import action creators from the other module (thus somewhat coupling the two directly), or have a separate piece of code that glues the two together. Using a saga like that is a very good approach for handling the "glue" aspect.

Related

What should be structure of Redux Store?

Redux.org tells to normalize your state but it creates some confusion.
It tells that we should have State in following format:
{
simpleDomainData1: {....},
simpleDomainData2: {....},
entities : {
entityType1 : {....},
entityType2 : {....}
},
ui : {
uiSection1 : {....},
uiSection2 : {....}
}
}
I can achieve this by two ways.
case 1: I have 3 pages, home, create, feeds page. Hence I can create homeReducer.js, createReducer.js, feedsReducer.js and each reducer will have simpleDomainData1, simpleDomainData2, entities , ui.
case 2: I can create separate reducers for each field like simpleHomeReducer.js, simpleCreateReducer.js, simpleFeedsReducer.js, entitiesReducer, uiReducer.js.
But I am failing to understand, which approach is right, and why ?
Hey kiran as you mentioned in question that you have 2 approaches to structure your reducer.
But, i will give you a new approach.
First of all it's sounds tricky but once you think a little it is
piece of cake for this project and future once also.
You should use combineReducers to combine your reducers to make it easy to use.
1. uiReducer
First you should create reducer for uiSection1 and in it you have all your logic from home Component, create Component and feeds Component for the uiSection1 only.
Just like that you create uiSection2 reducer and in it all your Component logic of your all pages related to uiSection2.
Now combine this 2 reducers to one reducer uiReducer.
2. entityReducer
Now, same thing do with entityType. Create 2 entityType reducer and combine them to one enitityReducer.
3. domainDataReducer
Now create each reducer for domain data and combine it to 1 reducer domainDataReducer
Now you have 3 reducers ui, entity and domainData.
4. rootReducer
Now, combine this 3 reducers to 1 reducer rootReducer and pass it down to index.js
And one last thing, you should do a seperate logic for all your
reducer action. And in this reducer action you can do api call to
backend as well.
This is the link to youtube video by TheNetNinja
There's plenty of information on this at redux.js.org for example basic reducer:
First and foremost, it's important to understand that your entire application really only has one single reducer function: the function that you've passed into createStore as the first argument.
And splitting reducer logic:
For any meaningful application, putting all your update logic into a single reducer function is quickly going to become unmaintainable.
These pages go as far as explaining a variety of techniques to split your reducer logic. There's no "standard" way of breaking reducers so both of your options are acceptable assuming you follow all the other rules of redux reducers.
My opinion
If your reducers are simpler per page, then go for option one. Something like a few actions to manipulate ui, entities and simpleData on each page. Otherwise if you have lots of actions for segments of your data, then split those into their own reducer as you've shown in your option two. For example lots of actions to manipulate ui alone, entities or simpleData.

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.

Replacing entire tree item in Redux with different reducer

Given the following redux state tree:
{
app: {...},
config: {...},
page_data: {...}
}
How would I replace the contents of page_data with an entire separate reducer depending on the page a user is on?
For example I could have three reducers user, products, competitions. If I switched from a user page to a product page I'd want the page_data branch to show:
{
page_data: {
productPage: {...}
}
}
with no reference to user as I don't want to bloat the app state and also don't need that data on the product page.
Note: I'm using combineReducers for reference.
Is this possible and what is the best approach?
Using same name for multiple reducers is definitely wrong. Not just it isn't supported, it's wrong practice. You can achieve this using FLUSH_PAGE_DATA action. Dispatching this action will flush the page data in all the reducers. It'll Look something like this.
case 'FLUSH_PAGE_DATA':
return { };
Then based on you active page, which you'll pass in every action, you can create different structure of page data.

Updating Data between two components in React

I am new to React and I don't know what's the best way to do this.
I have a list of cars and on clicking each row it should show slide to full page details of that car.
My code structure is:
I have App which renders two components. CarList and CarDetails. Car Details is hidden initially. The reason I rendered carDetails in app is because it's a massive fix template so I would like to render this once when app is loaded and only update it's data when each row clicked.
CarList also renders CarRow component which is fine.
Now my problem is I have a getDetails function on CarRow component which is making a call to get the details based on the car id. How to get carDetails component data updated ? I used
this.setState({itemDetails:data});
but seems state of the carRow is not the same reference as state in carDetails.
Any help?
This is a fundamental issue that lots of thought and man-hours has gone into in order to try and solve. It probably can't be answered, except on a surface level, in a StackOverflow post. It's not React-centric, either. This is an issue across most applications, regardless of the framework you're using.
Since you asked in the context of React, you might consider reading into flux, which is the de-facto implementation of this one-way data-flow idea in concert with React. However, that architecture is by no means "the best". There are simply advantages and disadvantages to it like everything else.
Some people don't like the idea of the global "event bus" that flux proposes. If that's the case, you can simply implement your own intermediate data layer API that collects query callbacks and A) invokes the callbacks on any calls to save data and B) refreshes any appropriate queries to the server. For now, though, I'd stick with flux as it will give you an idea of the general principles involved in having the things that most people consider to be "good", like a single source of truth for your data, one way flow, etc.
To give a concrete example of the callback idea:
// data layer
const listeners = [];
const data = {
save: save,
query: query
};
function save(someData) {
// save data to the server, and then...
.then(data => {
listeners.forEach(listener => listener(data));
});
}
function query(params, callback) {
// query the server with the params, then
listeners.push(callback);
}
// component
componentWillMount() {
data.query(params, data => this.setState({ myData: data }));
},
save() {
// when the save operation is complete, it will "refresh" the query above
data.save(someData);
}
This is a very distilled example and doesn't address optimization, such as potential for memory leaks when moving to different views and invoking "stale" callbacks, however it should give you a general idea of another approach.
The two approaches have the same policy (a single source of truth for data and one way data flow) but different implementations (global "event bus" which necessitates keeping track of events, or the simple callback method, which can necessitate a form of memory management).

Categories

Resources