ink icon indicating copy to clipboard operation
ink copied to clipboard

When setting a focus by default it can never be changed

Open manast opened this issue 4 years ago • 3 comments

Consider this code based on the example code https://github.com/vadimdemedes/ink/tree/master/examples/use-focus-with-id:

import { render, Box, Text, useFocus, useInput, useFocusManager } from "ink";
import { useEffect } from "react";
export const App = () => {
  const { focus } = useFocusManager();

  useInput((input) => {
    if (input === "1") {
      focus("1");
    }

    if (input === "2") {
      focus("2");
    }

    if (input === "3") {
      focus("3");
    }
  });

  // Sets default focus on element with id === "1"
  focus("1");

  return (
    <Box flexDirection="column" padding={1}>
      <Box marginBottom={1}>
        <Text>
          Press Tab to focus next element, Shift+Tab to focus previous element,
          Esc to reset focus.
        </Text>
      </Box>
      <Item id="1" label="Press 1 to focus" />
      <Item id="2" label="Press 2 to focus" />
      <Item id="3" label="Press 3 to focus" />
    </Box>
  );
};

const Item = ({ label, id }: { label: string; id: string }) => {
  const { isFocused } = useFocus({ id });

  return (
    <Text>
      {label} {isFocused && <Text color="green">(focused)</Text>}
    </Text>
  );
};

When running the app it will show the following:

 Press Tab to focus next element, Shift+Tab to focus previous element, Esc to reset focus.

 Press 1 to focus (focused)
 Press 2 to focus
 Press 3 to focus

However, it is not possible to change focus now, neither with "tab" nor with the number keys 1,2,3, the focus is actually changed but jumps back directly to the initial component with id === "1".

I have tried to put the call to focus inside the useEffect hook with the exact same results:

useEffect( () => {
  focus("1")
})

Any ideas, or is this just a bug?

manast avatar Apr 18 '22 10:04 manast

After some more experiments, it looks as if the component is re-rendered when pressing "tab", so that's why it goes back to the initial focus status, it kind of makes sense, but how to work around this issue?

manast avatar Apr 18 '22 11:04 manast

Ok, the solution is simply to use the "autoFocus" property. I paste here the code as a reference for others:

const Item = ({ label, id }: { label: string; id: string }) => {
  const { isFocused } = useFocus({ id, autoFocus: id === "1" });

  return (
    <Text>
      {label} {isFocused && <Text color="green">(focused)</Text>}
    </Text>
  );
};

manast avatar Apr 18 '22 11:04 manast

Going to re-open this one to investigate later.

vadimdemedes avatar Jul 24 '22 19:07 vadimdemedes