How to initialize the redux-form with dirty set true? - redux-form

I have a save button binded to the dirty state, when form is dirty, enable the button, otherwise disable it.
This is good for editing an item. But when I add an item, the save button is disabled by default, because dirty is false by default.
Is there a way to set the form to dirty state?

According to Action Creators documentation of redux-form, you can do it when you initialize data for your form:
import { initialize } from 'redux-form';
export const initializeForm = (formName, newData) => async (dispatch, getState) => {
await dispatch(initialize(formName, newData, false));
};
Also, you can set additional options in that function f.e
import { initialize } from 'redux-form';
export const initializeFormWithOldValues = (formName, newData) => async (dispatch, getState) => {
await dispatch(initialize(formName, newData, false, { keepSubmitSucceeded: true, keepValues: true }));
};

Nobody answered the question yet.
What I am doing now is to create a canSave prop to my component.
canSave && dirty can determine the visibility status of the save button.
When adding an item, canSave is set to true, save button is always visible.
When editing an item, canSave is false, the visibility is determined on dirty prop.
I'm still hoping to find a more elegant answer here.

You can control the save button state yourself. All you need to do is to not enable the Save/Submit button

Related

Redux Toolkit - how to block navigation?

I have no idea how you can implement blocking page navigation in redux-toolkit.
For example, before switching to another page, if some clause is false, then don't allow the switching.
How can this be done?
I found something that can be done via react-router Prompt.
Also, redux-toolkit supports the usePrefetch hook - but I'm not
sure if it is applicable here.
There are also history.push methods and stuff from the history
object.
I need to check the number of objects in my Queue before switching to another page. If there is more than zero, but do not allow to go to another page.
I've never worked with blocking navigation.
Tell me how this can be implemented specifically for the Redux Toolkit in the most optimal and beautiful way?
I found the solution here
Using history.block with asynchronous functions/callback/async/await
const useIsValidBlockedPage = () => {
const history = useHistory();
const { isValid } = useFormikContext();
useEffect(() => {
const unblock = history.block(({ pathname }) => {
// if is valid we can allow the navigation
if (isValid) {
// we can now unblock
unblock();
// proceed with the blocked navigation
history.push(pathname);
}
// prevent navigation
return false;
});
// just in case theres an unmount we can unblock if it exists
return unblock;
}, [isValid, history]);
};

AsyncStorage - error (React Native)

I have a problem with persisting user's data in a react-native application I am currently working on with my team. We have an app with a list of events and when you click on an event, it opens up the event details page and shows a menu to choose yes/no/maybe (attendance menu) which the user can toggle to select whether or not they are attending the event. Our code looks like this:
import React from 'react';
import { AsyncStorage, Text, View, StyleSheet, Picker } from 'react-native';
export class PickerMenu extends React.Component {
state = { choice: '' }
componentDidMount = () => AsyncStorage.getItem('choice').then((value) => this.setState({ 'choice': value }))
setName = (value) => {
AsyncStorage.setItem('choice', value);
this.setState({ 'choice': value });
}
render() {
return (
<View>
<Picker selectedValue = {this.state.choice} onValueChange = {this.setName} itemStyle = {styles.item}>
<Picker.Item label = "Yes" value = "yes" />
<Picker.Item label = "Maybe" value = "maybe" />
<Picker.Item label = "No" value = "no" />
</Picker>
</View>
);
}
}
We are running the build on Xcode. We want the user's choice to still be present after the page has been refreshed or the app has closed - this works HOWEVER,the choice that the user has selected, stays the same for every event. E.g. if they select 'Maybe' for event 1, the selection 'maybe' is highlighted for every other event in our app.
We had an idea of grabbing unique event IDs and implementing this into the AsyncStorage function but we are unsure of how to do this.
Any help will be great and much appreciated - thank-you!
In this case you need to use the async/await to store at the async storage
like this:
setName = async (value) => {
await AsyncStorage.setItem('choice', value);
this.setState({ 'choice': value });
}
Hope i have helped!
Some useful links for you:
https://facebook.github.io/react-native/docs/asyncstorage.html

react-redux together with components status

in a react UI I have a table component. You can edit one row of the table by clicking a edit button or you can add a new record by clicking a "new-record-button". When clicking the edit button an redux-action is triggered which takes the row and sets a visible property of a modal dialog. When the "new-record-button" is clicked an action is triggered which creates a new empty data item and the same modal dialog is triggered.
In the modal dialog I have several text components with onChange method.
in this onChange-method the data-item is written.
When to user clicks a save-button the edited dataItem is saved to the database.
So my code looks like:
const mapStateToProps = (state) => ({
dataItem: state.datItemToEdit || {},
...
});
...
handleTextChange(event) {
const {
dataItem
} = this.props;
const id = event.target.id;
const text = event.target.value;
switch (id) {
case 'carId': {
dataItem.carId = text;
break;
}
...
}
this.forceUpdate();
}
...
<TextField
...
onChange={event => this.handleTextChange(event)}
/>
I have several question regarding this approach. First I do not understand why in handleTextChange we can write to dataItem. It does work apparently.
dataItem.carId is set in the example code but I thought
const {dataItem} = this.props;
gives us a local read-only variable dataItem just to read from the props...
Next thing I think is a poor design. After reading in a book about react I think we should not write to props but only set a state.
In my example I get the the dataItem from the redux-state. The mapStateToProps maps it to the (read-only) props of the component, right?!. But I want to EDIT it. So I would have to copy it to the state of my component?
But where to do it?
Once in the state of my component I could simply call this.setState for the various text-fields and the component would render and I could abstain from forceUpdate(), right?!
Can someone explain how the redux status plays together with the component status and props for this example?
In redux or react, you shouldn't write to the props directly because you should keep your props as immutable. Redux forces us to use immutable state because state is a source of truth for the application. If the reference to state changes then only your app should render. If you'll mutate your state (objects) then the references don't get changed and your app doesn't know whether some state has been changed or not. React/Redux doesn't give you read-only objects automatically. You can mutate them anytime but as I told you, it can cause problems that Your app won't know when to re-render. If you want to have this read-only property inherently, you should probably use immutable.js
About your second question that you'll have to copy the props to the component's state and where you should do it. You should do it in the constructor of the component and you should use immutibility helper
import React from React;
import update from 'immutibility-helper';
class Modal extends React.Component {
constructor(props){
this.state = {
dataItem: dataItem,
};
}
...other methods
handleTextChange(event) {
const {
dataItem
} = this.props;
const id = event.target.id;
const text = event.target.value;
switch (id) {
case 'carId': {
this.props.updateItem(this.state.dataItem, text); //fire a redux action to update state in redux
this.setState(update(this.state, {
dataItem: {
carId: {$set: text},
}
});
break;
}
...
}
}
}
You wouldn't have to do forceUpdate in such case because the reference to state will change and the component will re-render itself.
Also, you can use forceUpdate in your application but personally I don't find it a great idea because when React/Redux is giving you the flow of state, by using forceUpdate, you're breaking the flow.
The last question is how redux and react state plays together. That is also a matter of choice. If I have a app level state, e.g., in your case you've some app level data, you should put that in your redux state and if you have a component level things, such as opening a modal or opening a third pane. That's the convention I follow but that can really depend on how you want to exploit react and redux state.
Also, in above code, I put the redux state in component state too (because you asked where to put that) but Ideally you should fire a redux action and update in redux state. In this way, you will restrict yourself from state duplication in react and redux.
import React from React;
import {updateItem} from './actions';
class Modal extends React.Component {
...other methods
handleTextChange(event) {
const {
dataItem
} = this.props;
const id = event.target.id;
const text = event.target.value;
switch (id) {
case 'carId': {
this.props.updateItem(this.props.dataItem, text); //fire a redux action to update state in redux
break;
}
...
}
}
}
const mapStateToProps = (state) => ({
dataItem: getDataItem(state), //get Data Item gets Data from redux state
});
export default connect(mapStateToProps, {updateItem: updateItem})(Modal);
in Actions:
updateItem = (dataItem, text) => dispatch => {
dispatch({type: 'UPDATE_ITEM', payLoad: {dataItem, text});
};
in Reducer:
export default (state = {}, action) => {
switch(action){
case 'UPDATE_ITEM': {
return {
...state,
dataItem: {
...action.dataItem,
carId: action.text,
}
};
}
}
}
In this way, your state will be pure and you don't have to worry about immutibility.
EDIT:
As constructor will be called only once, you should probably use componentWillReceiveProps so that whenever you render the component, you get the next updated props of the component. You can check whether the carId of dataItem is same or not and then update the state.
componentWillReceiveProps(nextProps){
if(nextProps.dataItem.carId !== this.props.dataItem.carId){
this.setState({dataItem: nextProps.dataItem});
}
}
You should only use redux when you want different, unrelated components in your app to know and share the specific state.
e.g. - When a user logs in to your app, you might want all components to know that user so you'll connect your different containers to the user reducer and then propagate the user to the components.
Sounds like in this case you have a classic use case for using the inner state.
You can use the parent of all TextFields to maintain all rows, edit them by index, etc.
Once you start using redux, it's really easy to make the mistake of transferring the entire state of the components to the reducers, I've been there and stopped doing it a while ago :)

jqGrid 'clearToolbar' without grid reload

I need to clear the toolbar without reloading the grid in my jqgrid. It should just reset the toolbar to its default values.
I tried using,
$("#TransactionsGrid")[0].clearToolbar();
My grid datatype:local and i don't use loadonce:true.
This made the toolbar clear and refresh the grid. I dont want that to happen.
Any ideas?
I find the question interesting.
To implement the requirement I suggest to use register jqGridToolbarBeforeClear to execute the handler only once. The handler should 1) unregister itself as the event handler and return "stop" to prevent reloading of the grid:
$grid.jqGrid("filterToolbar", { defaultSearch: "cn" });
$("#clearToolbar").button().click(function () {
var myStopReload = function () {
$grid.unbind("jqGridToolbarBeforeClear", myStopReload);
return "stop"; // stop reload
};
$grid.bind("jqGridToolbarBeforeClear", myStopReload);
if ($grid[0].ftoolbar) {
$grid[0].clearToolbar();
}
});
The corresponding demo shows it live.

kendo ui form update cancel button

I am trying to a simple kendo ui form with 'Save' and 'Cancel' buttons. I am using the Kendo.Observable to bind the data to the form.
The functionality I am trying to achieve is, if the 'Save' button is clicked, the form data will be saved. Else, if 'Cancel' is clicked the form will come back to read-only mode with the previous data that was present. To do this, I am first saving the model data in a 'originalvalue' property on click of Update button. If 'Cancel' is clicked, the 'fields' model data is restored to the 'originalvalue'. But the issue is that the , 'originalvalue' does not contain the original value. It gets updated when the user is editing during 'Save'.
The question is - how do I retain the original model data so that it can be refreshed on cancel?
Please find below the code. Appreciate your help, thanks.
<script type="text/javascript">
var viewModel = kendo.observable ({
updated: false,
originalvalue: {},
update: function(e) {
var original = this.get("fields");
this.set("originalvalue", original);
this.set("updated", true);
},
save: function(e) {
e.preventDefault();
if (validator.validate()) {
// make an ajax call to save this data
this.set("updated", false);
}
},
cancel: function(e) {
var original = this.get("originalvalue");
validator.destroy();
this.set("fields", original);
this.set("updated", false);
},
fields: {}
});
viewModel.set("fields", formArray);
kendo.bind($("#outerForm"), viewModel);
// prepare the validator
var validator = $("#outerForm").kendoValidator().data("kendoValidator");
I had to make the exact thing on a form I am currently developing. I am using a DataSource object for the data so I had to use cancelChange().
The thing I did there:
1. I made a Datasource with a schema:
... schema: {
model: {id: "id"}}
...
2. I got the object I was editing with the mapped id:
clientDataSource.cancelChanges(clientDataSource.get(this.get("contactID")));
where the ContactID is created in a setData function where I have passed the ID:
this.set("contactID", contactID);
As I may have notices and understood, you have another problem here where you arent using a DataSource but rather data for fields?
The problem here is that your originalValue is inside the Observable object and it is referenced to the variable original and thus it has observable properties. You should have the variable originalValue defined outside the observable object:
var originalValue;
var viewModel = kendo.observable ({ ...
And you should send the formArray also to that variable so you will have the defaults load before even the observable object is loaded such as:
originalValue = formArray;
viewModel.set("fields", formArray);
So when you need to cancel it you should have:
cancel: function(e) {
var original = originalValue;
validator.destroy();
this.set("fields", original);
this.set("updated", false);
},
I havent tested it but it should provide you some guidance on how to solve that problem.

Resources