title: Unit testing a memo component with React hooks library
body: <p>Am trying to perform unit tests with React hooks, but I haven't got a seamless approach, am using this library <a href="https://react-hooks-testing-library.com/usage/basic-hooks" rel="nofollow noreferrer">react hooks testing library</a>, and I want to ensure that I test a component but wrapped with a memo, inside it there are <code>useCallbacks</code>, I want to test them one by one, the component below is exported like this <code>export default memo(DateTimePicker); </code>  so how can I go deeper and test useCallback functions e.g <code>getValueFormat</code>, <code>setFormatter</code> etc.</p>
  <p>Below is the component I want to test:</p>
  <pre><code>const DateTimePicker = ({
    inputRefProp,
    options = undefined,
    datepicker = true,
    timepicker = true,
    placeholder = '',
    defaultValue = '',
    value: propValue = '',
    displayDateFormat = 'DD.MM.YYYY',
    displayTimeFormat = 'HH=mm',
    displayDateTimeFormat = 'DD.MM.YYYY HH=mm',
    value_format = undefined,
    onChange = undefined,
    onBlur = undefined,
    onBlurTyped = undefined,
    scrollInput = false,
    customInput = undefined,
    locale = 'de',
    maxDate = undefined,
    earlierDate = false,
  }: Props) =&gt; {
    const [value, setValue] = useState&lt;ValueType&gt;('');
    const [isInvalid, setIsInvalid] = useState(false);
    const [inputValue, setInputValue] = useState('');
    const [selected, setSelected] = useState(false);
    const inputRef = useRef(null);
  
    const getDisplayFormat = useCallback(() =&gt; {
      if (datepicker &amp;&amp; timepicker) {
        return displayDateTimeFormat;
      }
      if (datepicker) {
        return displayDateFormat;
      }
      if (timepicker) {
        return displayTimeFormat;
      }
      return displayDateTimeFormat;
    }, [displayDateFormat, displayTimeFormat, displayDateTimeFormat, datepicker, timepicker]);
  
    const getValueFormat = useCallback(() =&gt; {
      if (value_format) {
        return value_format;
      }
      if (datepicker &amp;&amp; timepicker) {
        return ISO_DATETIME_FORMAT;
      }
      if (datepicker) {
        return ISO_DATE_FORMAT;
      }
      if (timepicker) {
        return ISO_TIME_FORMAT;
      }
      return ISO_DATETIME_FORMAT;
    }, [datepicker, timepicker, value_format]);
  
    const setFormatter = useCallback(() =&gt; {
      $.datetimepicker.setDateFormatter({
        parseDate(date: Date, _format: string) {
          const d = moment.utc(date, _format);
          return d.isValid() ? d.toDate() : false;
        },
  
        formatDate(date: Date, _format: string) {
          return moment.utc(date).format(_format);
        },
      });
    }, []);
  
    const getValue = useCallback(
      (newValue: ValueType = '') =&gt; {
        if (newValue) {
          return moment.utc(newValue, getValueFormat()).format(getDisplayFormat());
        }
        return moment.utc(value, getValueFormat()).format(getDisplayFormat());
      },
      [getDisplayFormat, getValueFormat, value],
    );
  
    const onChangeHandler = useCallback(
      (newValue: string) =&gt; {
        setSelected(true);
        let currenIsInvalid = false;
        if (newValue) {
          try {
            const momentValue = moment.utc(newValue, true);
            if (!momentValue.isValid()) {
              currenIsInvalid = true;
            } else {
              const newInputValue = moment.utc(newValue).format(getDisplayFormat());
              setInputValue(newInputValue || '');
              if (onChange &amp;&amp; newInputValue) {
                onChange(moment.utc(newInputValue, getValueFormat()).toDate());
              }
            }
          } catch (e) {
            currenIsInvalid = true;
          }
        } else {
          setInputValue(newValue);
          if (onChange &amp;&amp; newValue) {
            onChange(moment.utc(newValue, getValueFormat()).toDate());
          }
        }
        setIsInvalid(currenIsInvalid);
      },
      [getDisplayFormat, getValueFormat, onChange],
    );
  
    const setComputedValue = useCallback(
      (momentValue: ValueType) =&gt; {
        const newValue = momentValue &amp;&amp; moment.utc(momentValue, getValueFormat()).toDate();
        setInputValue(momentValue ? getValue(momentValue) : inputValue);
        setValue(newValue);
        const $input = $(inputRefProp.current);
        if ($input) {
          $input.datetimepicker('setOptions', { value: propValue });
        }
      },
      [getValue, getValueFormat, inputValue, inputRefProp, propValue],
    );
  
    const initPlugin = useCallback(() =&gt; {
      if (inputRefProp &amp;&amp; inputRefProp.current) {
        let $input = $(inputRefProp.current);
        const inputCurrentValue = value || defaultValue;
        const defaultOptions = {
          formatTime: displayTimeFormat,
          formatDate: displayDateFormat,
          dayOfWeekStart: 1,
        };
        const pickerOptions = {
          allowDates: [],
          ...defaultOptions,
          ...options,
        };
        const allowDateTimes = resolvePath(pickerOptions, 'allowDateTimes');
        if (allowDateTimes) {
          pickerOptions.allowDates = allowDateTimes.map((dt: moment.MomentInput) =&gt;
            moment.utc(dt, ISO_DATETIME_FORMAT).format(pickerOptions.formatDate),
          );
        }
        setFormatter();
        setInputValue('');
        $input.datetimepicker('destroy');
        $input = $input.datetimepicker({
          ...pickerOptions,
          format: getDisplayFormat(),
          datepicker,
          timepicker,
          onChangeDateTime: onChangeHandler,
          value: inputCurrentValue,
          scrollInput,
          maxDate,
          lazyInit: true,
        });
      }
      // This function has to run only once since it initialize the datetimepicker plugin
      // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);
  
    useEffect(() =&gt; {
      $.datetimepicker.setLocale(locale);
      initPlugin();
    }, [initPlugin, locale]);
  
    useEffect(() =&gt; {
      if (propValue) {
        if (propValue !== value) {
          if (earlierDate) {
            setComputedValue(new Date());
          } else {
            setComputedValue(propValue);
          }
        }
      } else {
        setInputValue('');
        setValue('');
      }
      // This function should run whenever the propValue changes
      // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [propValue]);
  
    const onBlurHandler = useCallback(
      (newValue: ValueType) =&gt; {
        if (onBlurTyped &amp;&amp; !selected) {
          if (maxDate) {
            const compareDates = compareDesc(
              newValue as Date,
              moment(new Date(maxDate as string).getUTCDate(), getValueFormat()).toDate(),
            );
            if (!newValue || compareDates === 0 || compareDates === 1) onBlurTyped(newValue);
            else onBlurTyped(value);
          } else onBlurTyped(newValue);
        }
        if (onBlur) onBlur(newValue);
      },
      [onBlurTyped, onBlur, selected, getValueFormat, maxDate, value],
    );
  
    const onBlurInput = useCallback(() =&gt; {
      if (isInvalid) {
        setInputValue('');
        setValue('');
        onBlurHandler('');
      } else {
        if (!inputValue) {
          setInputValue('');
          setValue('');
          onBlurHandler('');
          return;
        }
        const newValue = inputValue &amp;&amp; moment.utc(inputValue, getValueFormat()).toDate();
        setComputedValue(inputValue);
        onBlurHandler(newValue);
      }
    }, [getValueFormat, inputValue, isInvalid, onBlurHandler, setComputedValue]);
  
    const onChangeHandlerInput = useCallback(
      (newValue: string) =&gt; {
        const re = /^[0-9]{0,2}\.?[0-9]{0,2}\.?[0-9]{0,4}$/;
        let currentIsInvalid = false;
        setSelected(false);
        const validValue = re.test(newValue);
        if (validValue) setInputValue(newValue);
        if (newValue &amp;&amp; validValue) {
          try {
            const momentValue = moment.utc(newValue, getDisplayFormat());
            if (!momentValue.isValid()) {
              currentIsInvalid = true;
            }
          } catch (e) {
            currentIsInvalid = true;
          }
        } else {
          currentIsInvalid = true;
        }
        setIsInvalid(currentIsInvalid);
      },
      [getDisplayFormat],
    );
  
    const onChangeInput = useCallback(
      (e: React.ChangeEvent&lt;HTMLInputElement&gt;) =&gt; {
        onChangeHandlerInput(e.target.value);
      },
      [onChangeHandlerInput],
    );
  
    const renderInput = useCallback(() =&gt; {
      if (customInput) {
        return React.cloneElement(customInput, {
          inputRef,
          placeholder,
          value: inputValue,
          onChange: onChangeInput,
          onBlur: onBlurInput,
        });
      }
      const inputEl = &lt;input type=&quot;text&quot; /&gt;;
      return React.cloneElement(inputEl, {
        inputRef,
        placeholder,
        value: inputValue,
        onChange: onChangeInput,
        onBlur: onBlurInput,
      });
    }, [customInput, inputValue, onBlurInput, onChangeInput, placeholder]);
  
    return &lt;div className=&quot;datetimepicker&quot;&gt;{renderInput()}&lt;/div&gt;;
  };
  
  export default memo(DateTimePicker);
  </code></pre>
  <p><strong>EDIT :</strong></p>
  <p>This what I have regarding unit tests, but they aren't working as expected :</p>
  <pre><code>it('should test DateTimePicker default value. ', () =&gt;{
        const { result } = renderHook(() =&gt; &lt;DateTimePicker/&gt;)
        expect(result.current.value).toBe('')
    });
  </code></pre>