• This is a simplified example:

    const Test = () =>
    {
      console.log("test called");  
      const [test, setTest] = React.useState('a'); 
      const [test2, setTest2] = React.useState('b'); 
       return  <div>
            <form>
                <input value={test} onChange={(e)=>setTest(e.target.value)} />
                <input value={test2} onChange={(e)=>setTest2(e.target.value)} />
            </form>
        </div>
    }
    
    registerBlockType( 'activities/activity-gap-fill', {
        apiVersion: 2,
        title: 'Activity Gap Fill',
        icon: 'universal-access-alt',
        category: 'design',
    
        edit: ( { setAttributes, attributes } ) => {
            
            const blockProps = useBlockProps();
            console.log("edit called", blockProps);
            return (
                <div { ...blockProps }>
                    <Test></Test>
                </div>
            );
        },
     
      
        save() {
            return null;
        },
    } );

    If I start typing in one of the form fields ‘test called’ is logged. This is expected. However – if the field loses focus then ‘edit called’ is logged. (NB. I don’t even have to start typing in the other field for ‘edit called’ to be logged – it is enough to just lose focus from the first field). Sometimes this just seems to happen randomly as well – even without the field losing focus.

    This is code from a CodePen:

    function Outer()
    {
      console.log("outer");
       return <Inner></Inner>
    }
    
    function Inner() {
    
      const [test, setTest] = React.useState('a');
      const [test2, setTest2] = React.useState('b');
    console.log("inner");
    
      return (
        <div>
      
          <form>
            <input value={test} onChange={(e)=>setTest(e.target.value)} />
            <input value={test2} onChange={(e)=>setTest2(e.target.value)} />
          </form>
        </div>
      );
    }
    ReactDOM.render(<Outer />, document.getElementById('app'));

    As you can see it is basically the same. This behaves as expected – i can enter data into the two fields and swap between them and I only ever see ‘inner’ logged to the console.

    It is something about the WordPress environment and this ‘edit’ function in a block which is causing this.

    Does anyone know what is causing it – and how can I stop it? Or; is it just something I have to live with?
    Thanks

    • This topic was modified 3 years, 2 months ago by Jan Dembowski. Reason: Formatting
    • This topic was modified 3 years, 2 months ago by justinwyllie.
    • This topic was modified 3 years, 2 months ago by justinwyllie.
    • This topic was modified 3 years, 2 months ago by justinwyllie. Reason: trying to get the formatting correct
Viewing 4 replies - 1 through 4 (of 4 total)
  • MK

    (@mkarimzada)

    Hi there,

    I think when your onChange event fires, the callback calls useState with the new value, then it’s passed to your text field as a prop. At this point, React decided to render a new component, which is why you lose focus and edit() is called.

    I think you could use components keys, specially the form and the input itself. Keys let React components to keep its identity. You could read more here.

    I hope this helps.

    Thread Starter justinwyllie

    (@justinwyllie)

    Thanks Murtaza
    In fact it is the other way round. It is losing focus on one for the form fields which causes my ‘edit’ function to be rerun.

    In the Gutenberg context the inner component Test is rendered on each character change in one of the form fields. This is expected in React with controlled fields; the state of Test component changes and so it is rerendered. But what I didn’t expect is that the enclosing function (‘edit’) is also rerun under certain circumstances e.g. when a field loses focus (as a result e.g. of the user clicking in the other form field) even though state does not belong to it. My pure React example (code supplied) shows that in such a case in a pure React context only the inner component (the one whose state is being updated) is rerendered – not the function which encloses it. So – there is something about the Gutenberg environment which I don’t understand. I’ve seen a suggestion that it could be something to do with Redux but I don’t know enough about Redux to know if that is the case. The implication seems to be that if state changes in any child component the whole component tree starting with whatever function edit returns will be rerun. (To be exact – not with each keystroke but with a field losing focus; something must be testing for onBlur on the field). Which is as I say unexpected and seems to have performance implications.

    • This reply was modified 3 years, 2 months ago by justinwyllie.
    • This reply was modified 3 years, 2 months ago by justinwyllie.
    MK

    (@mkarimzada)

    The re-running of edit() is completely normal. I think you missed the documentation that clearly states that a block is a javascript object with attributes that are automatically passed to edit() and save() and in your case, each time input changes the edit() gets invoked again – You can read more here: https://developer.www.ads-software.com/block-editor/how-to-guides/block-tutorial/introducing-attributes-and-editable-fields/#attributes

    I think a possible solution would be using RichText Component instead of input field just because the onChange gets called less frequently.

    jonathanmotes

    (@jonathanmotes)

    I’m having the same issue. @mkarimzada the link you referenced describes expected React behavior I think, where state changes would cause a re-render, but @justinwyllie and myself are having issues where things like component input focus are triggering re-renders of the Edit component – even on inputs that are NOT bound to attributes.

Viewing 4 replies - 1 through 4 (of 4 total)
  • The topic ‘Gutenberg Block constantly re-renders on state change in inner component’ is closed to new replies.