React custom hooks using refs as index counters
Looks to be fairly clean code.
Suggestions

Use string::slice or string::substring versus string::substr. substr isn't deprecated but also isn't recommended for use.


Warning: Although String.prototype.substr(…) is not strictly
deprecated (as in &quot;removed from the Web standards&quot;), it is considered
a legacy function and should be avoided when possible. It is not part
of the core JavaScript language and may be removed in the future. If
at all possible, use the substring() method instead.

setCurrentString(currentString.slice(0, currentStringIndex.current + 1));


Provide clearer parameter names, i.e. stringArray versus just strings, and setString or setCurrentString versus set. set alone can be confusing as it isn't very descriptive, i.e. &quot;is it a mathematical set?&quot; By naming a function with verbNoun it is clearer to a reader what it is and what it does.

const useTyper = (stringsArray, setCurrentString, defaultDelay = 100) =&gt; {


stringIdx and stringsIdx can cause mental gymnastics looking for the 's' to differentiate the two. Isolated from one another the names aren't terrible, but when used together within the same function it is more demanding. The names should also align closer to the variable they indicate an index for. Based on the previous parameter naming suggestion I recommend stringArrayIndex and currentStringIndex.
You also appear to do an extra iteration on the currentString with the conditional test currentStringIndex.current &gt; currentString.length. You can increment to the next string when the current string's index is equal to the length, currentStringIndex.current &gt;= currentString.length.
Similar conditional test for checking for the end of the strings array, when the index is. greater then or equal to the length you can quit. stringArrayIndex.current &gt;= stringArray.length

const useTyper = (stringArray, setCurrentString, defaultDelay = 100) =&gt; {
  const [delay, setDelay] = React.useState(defaultDelay);
  // index counters, normally named i and j if this were an imperative loop
  const currentStringIndex = React.useRef(0);
  const stringArrayIndex = React.useRef(0);

  const textTyper = () =&gt; {
    const currentString = stringArray[stringArrayIndex.current];

    setCurrentString(currentString.slice(0, currentStringIndex.current + 1));

    if (currentStringIndex.current &gt;= currentString.length) {
      // Done with string
      stringArrayIndex.current += 1;
      currentStringIndex.current = 0;
      if (stringArrayIndex.current &gt;= stringArray.length) {
        setDelay(null); // Ended.
      }
    }
    currentStringIndex.current += 1;
  };

  useInterval(textTyper, delay);
};

Good job, nice hook.