Repository: facebook/react
File: packages/react-native-renderer/src/ReactNativeAttributePayloadFabric.js (JavaScript)

Code before:
function diffProperties(
  updatePayload: null | Object,
  prevProps: Object,
  nextProps: Object,
  validAttributes: AttributeConfiguration,
): null | Object {
  let attributeConfig;
  let nextProp;
  let prevProp;

  for (const propKey in nextProps) {
    attributeConfig = validAttributes[propKey];
    if (!attributeConfig) {
      continue; // not a valid native prop
    }

    prevProp = prevProps[propKey];
    nextProp = nextProps[propKey];

    if (typeof nextProp === 'function') {
      if (typeof attributeConfig === 'object') {
        // When the config for a function prop has a custom process method
        // we don't assume its a regular event handler, but use the process method:
        nextProp = attributeConfig.process(nextProp);
        if (typeof prevProp === 'function') {
          prevProp = attributeConfig.process(prevProp);
        }
      } else {
        // functions are converted to booleans as markers that the associated
        // events should be sent from native.
        nextProp = (true: any);
        // If nextProp is not a function, then don't bother changing prevProp
        // since nextProp will win and go into the updatePayload regardless.
        if (typeof prevProp === 'function') {
          prevProp = (true: any);
        }
      }
    }

    // An explicit value of undefined is treated as a null because it overrides
    // any other preceding value.
    if (typeof nextProp === 'undefined') {
      nextProp = (null: any);
      if (typeof prevProp === 'undefined') {
        prevProp = (null: any);
      }
    }

    if (removedKeys) {
      removedKeys[propKey] = false;
    }

Review comment (suggestion):
It doesn't seem right that we process here before diffing - for other properties we first diff and then we process. Assuming that we have we have a `process` method here also doesn't match the strict validation we do in the rest of the method.

This whole method seems to invoke `process` at multiple different points though, so it seem hard to keep the default for functions.

Could you try something like this?

```
const attributeConfigHasProcess = typeof attributeConfig === 'object' && typeof attributeConfig.process === 'function';
if (typeof nextProp === 'function' && !attributeConfigHasProcess) {
  // keep as before
}

/* rely on process being invoked as it does for other methods */

Code after:

/**
 * diffProperties takes two sets of props and a set of valid attributes
 * and write to updatePayload the values that changed or were deleted.
 * If no updatePayload is provided, a new one is created and returned if
 * anything changed.
 */
function diffProperties(
  updatePayload: null | Object,
  prevProps: Object,
  nextProps: Object,
  validAttributes: AttributeConfiguration,
): null | Object {
  let attributeConfig;
  let nextProp;
  let prevProp;

  for (const propKey in nextProps) {
    attributeConfig = validAttributes[propKey];
    if (!attributeConfig) {
      continue; // not a valid native prop
    }

    prevProp = prevProps[propKey];
    nextProp = nextProps[propKey];

    if (typeof nextProp === 'function') {
      const attributeConfigHasProcess = typeof attributeConfig === 'object' && typeof attributeConfig.process === 'function';
      if (!attributeConfigHasProcess) {
        // functions are converted to booleans as markers that the associated
        // events should be sent from native.
        nextProp = (true: any);
        // If nextProp is not a function, then don't bother changing prevProp
        // since nextProp will win and go into the updatePayload regardless.
        if (typeof prevProp === 'function') {
          prevProp = (true: any);
        }
      }
    }

    // An explicit value of undefined is treated as a null because it overrides
    // any other preceding value.
    if (typeof nextProp === 'undefined') {
      nextProp = (null: any);
      if (typeof prevProp === 'undefined') {
        prevProp = (null: any);
      }
    }

    if (removedKeys) {
      removedKeys[propKey] = false;