Customize onChange Method

Hi everybody,

I am using json forms with a react native project and I wonder if there is any way to get more information about which data exactly changed? i.e. I want to set the date when an input into a specific textfield is done, so that I have to implement some logic which does not only concerns the data of the form. To achieve this, it would be very helpful to get information about which data exactly changed inside the onchange method. Currently, I get the whole data set of the form.

I hope that there is any way to get more specific information and would be grateful for some advice!

Hi @ConRad, at the moment this is not possible. The onChange emitter is very generic and doesn’t know what changed, only that something did.

As you’re using JSON Forms with React Native this means that you have a complete own renderer set anyway. Therefore you can just add your listening logic to your bindings, e.g. you could hand over a modified handleChange prop to your renderers which also notifies your integration about the changes which are coming or were just performed.

What exactly is the use case where you need to perform additional logic on the change of an input? What are the effects of the additional logic? If it also changes other parts of the form I would usually go with a a custom renderer approach, if applicable in your case.

Hi Stefan,

thank you for your fast reply! Until now, I created custom renderers for each component that I use, but I have no clue where I could modify the handleChange prop… Here is some example code how my custom renderers are working:

            <JsonForms
                schema={schema}
                uischema={uischema}
                data={data}
                renderers={renderers}
                cells={RNCells}
                validationMode={validationMode}
                onChange={({ _errors, data }) => modifiedHandleChange(_errors, data)}
            />
        </View>
// here some imports are done

const RendererInputField = ({
    schema, data, handleChange, uischema, path,
}) => {
    return (
          <InputField
                data={data}
                onChange={(ev) => handleChange(path, ev)}
            />
    );
};

export const inputFieldTester = rankWith(
    100,
    optionIs('format', 'customInputField'),
);

export default withJsonFormsControlProps(RendererInputField);

At which point should I modify the handleChange prop?

To answer your last question: I need to collect data about the exact time when specific data was recorded, so this is no data which would be displayed in the form, but is connected to data which is affected by the user input.

When I am logging the path and ev variable inside the renderer it gives me exactly the data I need, but this data is not the same inside the handleChange method that I am calling from the JsonForms-Tag. I tried to change the JsonForms to this:

<JsonForms
                schema={schema}
                uischema={uischema}
                data={data}
                renderers={renderers}
                cells={RNCells}
                validationMode={validationMode}
                onChange={({ path, ev }) => modifiedHandleChange(path, ev)}
        />

     const modifiedHandleChange = (path, ev) => {
         // implementation of different cases for different changes of data
         // here, path and ev are undefined
     }

But path and ev are undefined in this case.

Hi @ConRad, the onChange in the JsonForms component and the handleChange injected into the renderers are completely independent which is why you can’t find a path and/or updateFunction in the onChange of the JsonForms component.

The handleChange is provided by the withJsonFormsControlProps HOC. One way of modifying it is by placing your own HOC in between. For example like this withJsonFormsControlProps(withHandleChangeEmitter(RendererInputField)). The withHandleChangeEmitter receives the handleChange from withJsonFormsControlProps and can pass down a different handleChange to the RendererInputField. In the handleChange which is passed down you can add any behavior you want and then just call the original handleChange. This way the renderer code does not need to be changed at all.

Hi Stefan,

thanks again for your helpful answer! :slight_smile: Actually, I already tried to write my own HOC, but I was quite unsure about the structure. Do I have to pass all the props of the withJsonFormsControlProps to the wrapped component (or is there maybe an example for an custom HOC, unfortunately, I did not found anything here)?

Maybe this is not the solution you meant and as the procedure of the change handling with Json Forms isn’t yet clear to me, this might be a stupid question, but would it be possible to keep the “modifiedHandleChange” method inside my App.js (where I am integrating the JsonForms) and give it as prop to my own HOC? Or do the implementation of the modified handleChange must happen inside the HOC completely? If so, I am wondering how I could address the data inside my App.js which I want to change as well.

I hope, the question is not to confusing and I could make my problem clear.

Best regards!

Hi @ConRad, that’s exactly right. You need to pass everything you don’t touch, otherwise it’s not available afterwards.

Within your custom HOC you can consume arbitrary data or callbacks via React contexts.

This is a HOC which does nothing. It takes a Component and returns a new anonymous component which just passes through all the props.

const withHandleChangeEmitter = (Component) => {
  return (props) => {
    return <Component {...props} />
  }
}

With that in place we can add your custom behavior

const withHandleChangeEmitter = (Component) => {
  return (props) => {
    // consume your context which you set up outside of JSON Forms
    const myContextInstance = useContext(MyContext);
    const myEmitter = myContextInstance.myEmitter;

    // create a new handleChange method which first does the regular update and then tells the emitter which data changed
    const handleChangeAndEmit = (path, updater) => {
      props.handleChange(path, updater);
      myEmitter(path);
    }

    // pass down all props as they are but overwrite handleChange
    return <Component {...props} handleChange={handleChangeAndEmit} />
  }
}
1 Like

I thought this would be the ticket — but I don;t understand enough to make it work.
First — why would we want notification of the path that has been ‘touched’. Lot’s of reasons.

  1. Let’s say you have a dependent field and if field A is changed, field B is invalid.
  2. Let’s say you have a oneOf whose elements need to be filtered by another field? i.e. Countries is filtered by Regions. When regions change, countries is invalidated and the oneOf enum thing is filtered.

So – as for the code. I wrapped, JsonForms in the code above (changing handleChange, for onChange) but it doesn’t pass me a path, it passes me the whole data structure. I’m guessing I need to apply the HOC lower in the chain. But don’t know how.

Any chance of a full react material ui example…that shows how to trigger an event, and give that event the path and the new value?

I don’t see why a specific notification is needed here.

I generally recommend to implement a custom renderer here which consumes the dependent data. As it’s then always rerendered when its own data or the dependent data changes there is no need for such a specific notification.

It needs to be applied on every renderer you want to modify.