I'm wondering what is the best way to implement CRUD operations in React Redux in case of List of entities.
Requirements:
1. Every row contain an entity
2. Changing a value in a text input trigger a PUT call to BE
3. Every row has Delete operation trigger a DELETE call to BE
4. Form has an ADD action trigger a POST call to BE
Notes:
1. No single Submit button
I thought about Redux Form for that but since I don't have a single submit operation I find it less appropriate (Feel free to correct me)
You can use Redux Form for that. If you setup a validator for the form, you can define if a field is dependent on another. Redux Form will run this validator for each change in any field.
You can setup something like Redux Observable or Redux Thunk to submit the form on Redux Form's CHANGE action when there are no validation errors in the form.
This way, you will submit the form on change, only if all the related fields are also filled.
I'm new to Redux. This is how I would do it. I will keep a state in the reducer which is called "personList". I will put all components in a container and map the personList to a property of the container called "propPersonList". When I create the ListItemComponent, I would also pass the index of the person in the list to the component as a property "propIndex". The onChange handler of the two inputs will dispatch an action with payload {index: this.props.propIndex, value: event.target.value}, then trigger a PUT request to backend (use lodash module to throttle it instead of triggering it everytime the input changes). The reducer will take care of the actions and update the state according to the payload. The add button will dispatch another action and the reducer will simply add an empty entity to the state, then trigger a request to the backend. The remove button will dispatch an action with the index as the payload, and the reducer will remove the corresponding entity from the list.
Related
I want to store the value of fields, upon update, in a redux state property. Is there a way to do this globally for all the form data or do I need to dispatch a separate reducer for each controlled component upon onChange/onValueChange/onTextChange?
By the way, I'm working in React Native, if that matters (thus using some specific patterns such as control instead of register).
Extra info about my form and redux state:
my form has nested children, each containing various custom controlled components. In each of those controlled components, the local form state is updated accordingly (either by passing the new value to setValue if it's a custom input).
After getting my local form data updating properly, I persisted the state in redux by dispatching a reducer in onSubmit. Because this form creates data associated with one item from a list of items, the reducer stores the form data in a parent object located at state.listOfItems[index].formData.
If an action removeItem(itemId) is fired from the UI it should first delete the corresponding item from the state and then call the associated saga. If the saga fails it should reset the state to it's original state.
I was wondering if you can append information to the action payload within the reducers then fire a failed action from the saga with the same payload. This is assuming that the reducer will always be fired first (Is it true?)
You shouldn't dispatch from inside your reducer. A reducers job is to take current state, an action, and return new state from the reduction of the two. That's it. What you're trying to do can be accomplished by a strategy like the following:
Use itemId to get a reference to the item and store it locally.
Dispatch action to remove item from store based on id.
Call associated saga.
If it fails, add locally stored item from step 1 back into the store. If successful, remove reference to item from step 1.
Looking for some advice when working with Redux, and Angular. I've been researching Redux for the past couple of days and really think its a great way to store application data. The part I'm having trouble with is whether to persist everything within the store or only certain parts. Ideally, I think the entire application should be running through the store, but for forms this seems very tedious.
For example, lets say I'm working with a form to add a new product. Here are some of my pain points.
I would like to keep the User Reducer (store) separated from the actual form state. Should I create a separate form reducer per component?
Having to persist every input field back to the store sounds like a lot of work. I've seen the library redux-form simplifies this, but is intended for React.
Anyone have any good advice when it comes to creating forms in Angular with Redux?
The answer is "it depends". Also, the assumption is that you''re convinced of the benefits of one-way data flow and redux, so prefer redux over two-way data binding if given the choice.
Uber-simple form (no validation, no complex relationships with other state). Then you could "go naked" and directly hook up the inputs to redux. In our use case, we actually decided to go with Angular forms because we figured it handles edge cases (IE and safari mobile).
Don't need every form change in redux state. Then the form submit can dispatch an action to update redux state. Things get tricky if the form needs to change in response to redux state. See below.
You do need every form change in redux state. Angular forms do not have a form#ng-change, so one strategy is to attach an ng-change to every input that dispatches an action to update the redux state. (Yes, it is error prone because it easy to forget to use ng-change, meanwhile the app appears to work.) Again, things get tricky if the form needs to change in response to redux state. See below.
Updating the form in response to redux state change
The common use case is actually very simple. A concrete example will help---suppose the form tracks app settings, meanwhile app settings exist as redux state. That is, there is a two-way data binding between the Angular form and the redux state. This is probably the common use case.
In this case, the solution is to proceed as before: update redux state from the Angular form by dispatching update actions, and update the Angular form from redux state via #mapStateToThis.
Angular ----dispatch actions-----> Redux
Form <----mapStateToThis-------- State
The only gotcha is to not pass the Redux state directly to the Angular form i.e., deep clone the data or use ImmutableJS.
The other common use case is to implement a "form reset", that is, reset the form to a "pristine" state after pressing a button, for example. Again, a concrete example will help:
Suppose that app state (redux state) tracks whether the state is pristine via a flag app.pristine. (To clarify how app.pristine works, it works as expected, that is, it changes to false as soon as any value changes, and changes to true only when explicitly set to true.)
First, as far as I know, Angular doesn't automagically keep track of the "initial" state. You have to do it yourself and you may as well put that state in redux. Then, the initial form values are just the app settings when app.pristine is false. (If you're thinking of putting this in #mapStateToThis, don't. Doing side effects in a transform function seems weird.) A better way is to use an asynchronous action, namely the form onChange listener:
// thunk example
onFormChange(newForm) {
return (dispatch, getState) => {
const appSettings = getState().appSettings;
const appIsPristine = appSettings.pristine;
// this will fire once because the next action will set it to false
appIsPristine && dispatch(initForm(appSettings)));
dispatch(updateAppSettings(newForm));
};
},
The reset action works as you would expect (which I won't outline).
To conclude, I should add that the above assumes that only the Angular form can dirty the app settings---otherwise, the initial form values may never be stored. If that's the case, then one idiomatic solution is to create a custom middleware that sets the initial form value whenever app.pristine changes to true. (Alternatively, use an Angular watch.)
I am trying to validate data in a redux store for a form. What is the best way to go about this. I have had a few ideas so far:
A validate action has to be bound to some sort of listener like onBlur or onChange. Once a form component blurs it is then validated. However, the validate function then can 1) only validate that field in isolation or 2) must be passed the full form state. Also, action creators cannot/should not have access to the new state, so I can only update existing data, and I cannot bind the validators to something that updates the state. One upside, is that I can perform asynchronous validation by calling a validator action creator, and returning a promise (using redux-promise/thunk).
Reducer validates it own data. Part 1 of the reduce would update the data, and Part 2 would analyze the state and update the errors. However, this goes against reducer purity, as updating one part of the state then updates another part the state. So I have to ensure the validator runs after the state is updated.
Use selectors to derive errors from state. The data itself does not change if its invalid/valid, just errors are shown. However, since selectors are synchronous, I cannot do any asynchronous validation.
You can store all your fields in reducer with shape like:
"fields": {
"first_field": {
"error": null, //stores error that should be shown in form
"touched": false, //becomes true when onBlur event fired for this field,
"value": "", //input value
},
...
}
In this case you can run validation for whole form and validate all fields that have touched: true. Actually, all problems that you describe are solved in redux-form and I'd suggest you to use it.
Question
When I load a React Component, how I can add an object to a store, and then immediately load it into the component's state using the Flux pattern?
Background
I have a Flux store (TableStore) that manages objects called Tables, and React components called TableList and TableEditor.
The TableList component lists the tables in the store, and each item has an edit button that navigates to /tables/editor/:tableId, which loads the TableEditor component, and TableEditor gets the table from the store via the tableId in the URL.
TableList also has a "Create new table" button. Which navigates to /tables/editor/new. When this URL is loaded, I need to add a new Table to the store and then load it into TableEditor's state.
Using the Flux pattern, components aren't supposed to call functions like this directly; they're supposed to call Actions which dispatch actions to the Store. Unfortunately, if an action is called in a component's getInitialState(), the function in the store is executed before componentWillMount() is executed in the component, and the component misses the change event from the store, so my TableEditor component doesn't load the table from TableStore.
I assume this is a pretty common need, so there must be a standard way of getting around this (I hope).
Any ideas?
I think you are making a bit of confusion with the flux pattern.
When I load a React Component, how I can add an object to a store, and
then immediately load it into the component's state using the Flux
pattern?
how I can add an object to a store?
You should always add an object to store emitting a new create action. The store register itself to the dispatcher to listen to actions. When a action is dispatched, an handler is called to perform that action. This is the only way to mutate the state of a store. For example, in your case, the view should emit a TABLE_CREATE action. The TableStore should list for table TABLE_CREATE and save the new table. A bit of code:
Table List View
onSave: function(table) {
TableActions.create(table);
}
Table Actions
var TodoActions = {
create: function(text) {
AppDispatcher.handleTableAction({
actionType: TableConstants.TABLE_CREATE,
table:table
});
}
Table Store
switch(action.actionType) {
case TableConstants.TABLE_CREATE:
text = action.table();
if (!isEmpty(table)) {
create(table);
TableStore.emitChange();
}
break;
As you can see, the above code is following the flux pattern:
VIEW --(Action)--> DISPATCHER --(Action)--> STORE
Immediately load it into the component's state using the Flux pattern?
You fetch it directly from the store. We can read the stores from the views as we are not performing any state mutation. State mutation should be propagate by means of actions. Therefore, your component should use the function getInitialState to read the value from the TableStore. Probably, you need to use some sort of id. Code:
getInitialState() {
return {
table : TableStore.getTableById(id)
}
}
According to what you wrote, you have a store which contains all tables.
When a user clicks the button create a new action should be dispatched to the table store in order to add the new table to the store. The table store should be listening for this action. Now, the user is likely redirect to the editor page where he can edit the newly table. The edit table view fetches the value from the store in the getInitialState and reders the edit. Make sense?
One solution you could try is to always keep a new and empty table object (or just a 'free' table ID) in your table store.
Couple the NEW button (or whatever it is that user does to create a new table) to a /tables/editor/:tableId that holds your new empty Id.
That way, your do not need a fancy redirect from /new to /:tableId etc.