question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

redux form with undo

See original GitHub issue

Hello,

I are really new to react/redux and redux-form so I might have an error in reasoning. Anyway, what I am trying to do is writing my own decorator that should give my redux form an undo functionality. I didn’t want to mess with redux form internal state through reducer.plugin() therefore I defined my own state “undoableForm” which resides at the same level like “form”. My default state looks like this (the entire source code is attached):

const defaultState = {
  undoableForm: {
    initialValues: {name: 'test'},
    currentValues: {name: 'test'},
    previousValues: [], //undo
  }
}

Every time a CHANGE happens (I listen to redux-form CHANGE event), I save the old value in previousValues and update currentValues. My undoableForm decorator gets the previousValues through connect and upon that it provides “undoable” (true/false) and “undo” (function) to the decorated redux-form. In my form I defined an UndoButton component that gets their new props and gets activated/deactivated whenever “undoable” changes, and by clicking it “undo” should be called.

I have the following problems:

  1. whenever I change the value in the input-box the whole form gets unmounted/mounted. Every field gets unregistered and then registered again. The problem comes from the fact that I am providing previousValues through connect. But I have to do that since my decorator depends on this to calculate “undoable”.
  2. I must set destroyOnMount to false otherwise the value in the input-box won’t get updated.
  3. In my own state “undoableForm” everything seems to be fine but I still don’t know how to update the values in state.form when I click on “undo”-button.

I am not sure whether I am doing something wrong … maybe the structure is wrong or maybe a decorator over redux-form is not the right way to do that. But I am sure that I need your help 😃 Thank you very much in advance!

undoable-redux-form.zip

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Comments:6 (2 by maintainers)

github_iconTop GitHub Comments

3reactions
wmertenscommented, Dec 7, 2017

I still think this would be a great addition to RF…

2reactions
offboarded-x233534commented, Nov 15, 2016

Hi, I had the same problem and came up with this solution.

Prerequisites:


EditableCV.js

import React, { PropTypes } from 'react';
import EditCvReduxForm from './EditCvReduxForm';
import { Provider } from 'react-redux';
import { reducer as formReducer } from 'redux-form';
import { createStore, combineReducers, compose } from 'redux';
import undoable, { ActionCreators } from 'redux-undo';
const store = createStore(
  combineReducers({ form: undoable(formReducer) }),
  compose(
    window.devToolsExtension ? window.devToolsExtension() : f => f
  )
);


class EditableCV extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      initialFormValues: {
        username: 'foo',
      },
      counter: 0,
    };
    this.undoStep = this.undoStep.bind(this);
    this.redoStep = this.redoStep.bind(this);
  }
  isUndoPossible() {
    let length = 0;
    if (store.getState() !== undefined &&
        store.getState().form !== undefined &&
        store.getState().form.past !== undefined) {
      length = store.getState().form.past.length;
    }
    console.log('#isUndoPossible', length);
    return length > 3; // Must be 3 since Redux-Form sets initial values we do not want to undo!
  }
  isRedoPossible() {
    let length = 0;
    if (store.getState() !== undefined &&
      store.getState().form !== undefined &&
      store.getState().form.future !== undefined) {
      length = store.getState().form.future.length;
    }
    console.log('#isRedoPossible', length);
    return length > 0;
  }
  undoStep() {
    if (this.isUndoPossible() === true) {
      store.dispatch(ActionCreators.undo());
      this.setState({ counter: this.state.counter + 1 });
    }
  }
  redoStep() {
    if (this.isRedoPossible() === true) {
      store.dispatch(ActionCreators.redo());
      this.setState({ counter: this.state.counter + 1 });
    }
  }


  render() {
    const { id } = this.props;
    return (
      <div>
        {id}
        <hr />
          <Provider store={store}>
            <EditCvReduxForm
              onSubmit={() => console.log('foo')}
              initialValues={this.state.initialFormValues}
              undo={this.undoStep}
              redo={this.redoStep}
              isUndoPossible={this.isUndoPossible}
              isRedoPossible={this.isRedoPossible}
              counter={this.state.counter}
            />
          </Provider>
      </div>
    );
  }
}

export default EditableCV;

EditableCV.js

import React from 'react';
import { Field, reduxForm } from 'redux-form';
const renderInput = field => (  // Define stateless component to render input and errors
  <div>
    <input {...field.input} type={field.type} />
    {field.meta.touched && field.meta.error && <span className="error">{field.meta.error}</span>}
  </div>
);

//
// WARNING: THIS IS REDUX FORM 6.x
// READ: http://redux-form.com/6.2.0/docs/MigrationGuide.md/
//
const EditCvReduxForm = (props) => {
  const { handleSubmit, pristine, reset, submitting, redo, undo, isRedoPossible, isUndoPossible, counter } = props;
  return (
    <div>
      <form onSubmit={handleSubmit}>
        {pristine} {submitting}
        <hr />
        {counter}
        <div>
          <label htmlFor="username">Username</label>
          <Field
            name="username"
            component={renderInput}
            type="text"
          />
        </div>

        <div>
          <label htmlFor="password">Password</label>
          <Field
            name="password"
            component={renderInput}
            type="password"
          />
        </div>

        <button type="submit">Submit</button>
        <button onClick={reset}>reset</button>

      </form>
      <hr />
      <button
        onClick={undo}
        disabled={!isUndoPossible()}
      >
        undo
      </button>
      <button
        onClick={redo}
        disabled={!isRedoPossible()}
      >
        redo
      </button>
    </div>
  );
};
EditCvReduxForm.propTypes = {
  handleSubmit: React.PropTypes.func,
  pristine: React.PropTypes.bool,
  reset: React.PropTypes.func,
  submitting: React.PropTypes.bool,
  undo: React.PropTypes.func,
  redo: React.PropTypes.func,
  isRedoPossible: React.PropTypes.func,
  isUndoPossible: React.PropTypes.func,
  counter: React.PropTypes.number,
};

// Decorate the form component
export default reduxForm({
  form: 'editCvReduxForm',
  getFormState: (state) => state.form.present,
  enableReinitialize: true,
})(EditCvReduxForm);

It looks like this and supports:

  • Undo until initial state is restored
  • Redo
  • Reset Button of redux-form working

ezgif com-optimize 1

Read more comments on GitHub >

github_iconTop Results From Across the Web

Implementing Undo History - Redux
In this part of the recipe, you will learn how to make a small "todo list" app logic undoable.
Read more >
redux form with undo · Issue #2085 - GitHub
In my form I defined an UndoButton component that gets their new props and gets activated/deactivated whenever "undoable" changes, and by ...
Read more >
How to Implement Undo in a React + Redux Application
One is extremely simple; we implement undo in the reducer, which pushes dispatchable objects onto the stack with little additional code. The ...
Read more >
Initializing from State - Redux Form
You may only initialize a form component once via initialValues . If you wish to change the "pristine" values again, you will need...
Read more >
React Hook to Allow Undo/Redo - Medium
With undo/redo functionality, FormBlob is one of a select few no-code form builders that gives you the flexibility to edit your forms without ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found