react-ace icon indicating copy to clipboard operation
react-ace copied to clipboard

setState in onChange will block the editor

Open dyf102 opened this issue 8 years ago • 14 comments

  onChange(newValue) {
    this.setState({ code: newValue });
    console.log('change', this.state.code);
  }

dyf102 avatar Mar 22 '17 18:03 dyf102

I have what I think is a similar problem. I put the text content of the editor in the state and set the value to the text that is in the state.

<AceEditor
    value={this.state.text}
    onChange={(newValue) => this.textChanged(newValue)}/>

This doesn't work properly though. I think that the editor is being re-initialized at each step which isn't much good :(

I'm still working exactly what's going on.

mjmdavis avatar Mar 23 '17 07:03 mjmdavis

I think that the root of this issue is that if you change the text the component always updates.

One solution would be to add a shouldComponentUpdate() that only updates when properties have changed OR when the text property is not equal to the text displayed in the editor.

mjmdavis avatar Mar 23 '17 07:03 mjmdavis

or not, apparently I still don't really get this :grin:

mjmdavis avatar Mar 23 '17 10:03 mjmdavis

I'm having a similar issue

edit: I found a workaround for my particular issue. I was having issues trying to capture the input using a setState. It appears that having a defaultValue was causing some sort of conflict. (which makes sense I suppose?) I used value instead and now onChange and the editor works as (at least how I) expected.

Not sure if this is what @mjmdavis and @dyf102 were experiencing.

mcfungster avatar Apr 04 '17 01:04 mcfungster

I was always using 'value'. I think I was just being silly.

mjmdavis avatar Apr 04 '17 07:04 mjmdavis

Anyone find a solution for this issue?

brendanmcgivern avatar Apr 13 '17 19:04 brendanmcgivern

I'm having this same issue.

mbrochstein avatar May 05 '17 14:05 mbrochstein

@mbrochstein I was able to find a workaround by using Reacts shouldComponentUpdate method.

shouldComponentUpdate(nextProps, nextState) {
    if (this.state.aceEditorValue !== nextState.aceEditorValue) {
      return false
    } else {
      return true;
    }
  }

Then for my onChange method for the <AceEditor />

onChange = (newValue) => {
    this.setState({
      aceEditorValue: newValue
    });
  }

This will update the state of aceEditorValue with whatever is entered into the editor without re-rendering the entire component. Works for my use case.

brendanmcgivern avatar May 05 '17 15:05 brendanmcgivern

@brendanmcgivern Thanks! I think you flipped your comparators in the shouldComponentUpdate function, but once I switched that to ===, that works perfectly!

mbrochstein avatar May 05 '17 15:05 mbrochstein

What about this one? It works well in my project.

THE PARENT COMPONENT

    // other methods ...
    eventMDChange(value) {
        let _html = Marked(value)
        this.setState({ markdown: value, html: _html })
    }
    // other methods ...

THE EDITOR

<AceEditor
    onChange={ (raw)=>{this.eventMDChange(raw);} }
    value={ this.state.markdown }
    />

bin16 avatar May 12 '17 08:05 bin16

So the issue here is we're tapping into ace editor's actual change event rather than listening and rerendering. It sounds like you want to be able to tap into the period of time when a change is happening. We could have another hook in componentWillReceiveProps that prevents an update. Personally, i think @brendanmcgivern 's solution is the cleanest though.

securingsincity avatar May 15 '17 03:05 securingsincity

Same issue here :/

Sjoerd avatar Nov 24 '17 19:11 Sjoerd

Well, this is a huge bug and it is still there. I tried to use react-ace with https://github.com/jaredpalmer/formik and behaviour is the same, it just stucks.

<AceEditor
mode="yaml"
theme="solarized_light"
onChange={value => {
  setFieldValue("content", value);
}}
tabSize={2}
editorProps={{ $blockScrolling: true }}
value={''}
width="100%"
/>

upd This looks not nice, but it worked out for me. The key issue, as I understand, is that you better not set state or do anything with the value right from the ace's onChange (inline). You better do it in the different component's function.

import React, { Component } from "react";
import { Formik, Form, Field } from "formik";

class AcePlusFormik extends Component {
  state = { content: "" };

  /**
   * Special treatment for ace content
   * @param {string} value
   */
  setContent(value) {
    this.setState({ content: value });
    this.setFieldValue("content", value);
  }

  render() {
    <Formik
      initialValues={{ title: "", content: "" }}
      onSubmit={values => console.log(values)}
    >
      {({ touched, setFieldValue }) => {
        this.setFieldValue = setFieldValue;
        return (
          <Form>
            <Field name="content">
              {({ field }) => (
                <React.Fragment>
                  <AceEditor
                    mode="yaml"
                    theme="solarized_light"
                    onChange={value => {
                      this.setContent(value);
                    }}
                    tabSize={2}
                    editorProps={{ $blockScrolling: true }}
                    value={this.state.content}
                    width="100%"
                  />
                  <input type="hidden" {...field} />
                </React.Fragment>
              )}
            </Field>
            <button
              className="button is-primary is-pulled-right"
              style={{ marginTop: "10px" }}
              type="submit"
            >
              Save
            </button>
          </Form>
        );
      }}
    </Formik>;
  }
}

PavelPolyakov avatar Nov 22 '18 06:11 PavelPolyakov

Are there any solutions or workaround for functional components?

It happens when I add props debounceChangePeriod={500}

ddt313 avatar Dec 26 '23 04:12 ddt313