Проблема с нулевым состоянием использования, когда состояние не равно нулю в эффекте использования

#javascript #reactjs #typescript #react-hooks #react-functional-component

Вопрос:

некоторые проблемы с состоянием в TypeScript реагируют.

Дочерний компонент передает объект » терминал’ Родительскому через переданную функцию с именем returnTerminal() . Этот терминальный объект затем сохраняется как состояние использования _object . useEffect() говорит, что _object в конечном итоге это не равно нулю, но callback() постоянно утверждает, что _object это равно нулю, и я не уверен, почему.

Родительский компонент

 const Parent: React.FunctionComponent<ParentProps> = () => {
  const [_object, set_object] = useState<Terminal>(null);

  const handleGetTerminal = (terminal: Terminal) => {
    set_object(terminal);
  };

  useEffect(() => {
    if (_object !== null) {
      _object.writeln("_object is not null"); // This prints just fine
    }
  }, [_object]);

  return (
    <Child
      returnTerminal={(term) => handleGetTerminal(term)}
      callback={() => {
        console.log(_object === null); // This returns true - the object is null for some reason???
      }}
    />
  );
};
 

Дочерний компонент

 const Child: React.FunctionComponent<ChildProps> = (props) => {
  const { callback, returnTerminal } = props;

  // The ref where the terminal will be stored and returned
  const ref = useRef<HTMLDivElement>(null);
  // The xterm.js terminal object
  const terminal = new Terminal();

  useLayoutEffect(() => {
    // Calls whenever terminal is typed into
    if (callback) terminal.onData(callback);

    // Mount terminal to the DOM
    if (ref.current) terminal.open(ref.current);

    // Pass the terminal to the parent object
    returnTerminal(terminal);
  }, []);

  return <div ref={ref}></div>;
};

export default Child;

 

callback() Всегда возвращает значение _object null, независимо от того, как долго я жду.

Комментарии:

1. Комментарий: console.log(_object === null); // This returns false - the object is null for some reason??? кажется неправильным. Это false потому, что у него есть значение, оно НЕ равно null ?

2. useLayoutEffect обратный вызов крючка выполняется только один раз и закрывается над областью callback и terminal в области. const terminal = new Terminal(); также создает новый терминал каждый цикл рендеринга, это намеренно?

3. Можете ли вы попробовать заменить _object === null на typeof _object === "object" amp;amp; _object !== null" , потому null что на самом деле считается типом Object в JS

4. Эта закономерность не является распространенной. Странно, что у вас есть родитель, который имеет значение ребенка. Было бы приятнее, если бы родитель держал объект терминала и передавал его ребенку вместо того, чтобы родителю присваивалась переменная от ребенка

5. @Yooooomi это может быть общий родитель для разных компонентов, которые используют это значение, в этом нет ничего странного.

Ответ №1:

Ответом стало использование useImperativeHandle и forwardRef. Вот готовый код.

Родитель

 import Child from "./child";

interface ParentProps {
  name?: string;
}

const Parent: React.FunctionComponent<ParentProps> = () => {
  type ChildHandle = ElementRef<typeof Child>;
  const childRef = useRef<Child>(null);

  let _object: Terminal;  // same Terminal type that Child uses

  useEffect(() => {
    if (childRef.current) {
      _object = childRef.current.get(); // imperitive handle function
      _object.writeln(“_object loaded”);
    }
  }, []);

  return (
    <Child
      ref={childRef}
      callback={() => {
        terminal.writeln("_object is not null”);  // THIS finally works
      }}
    />
  );
};
 

Ребенок

 interface ChildProps {
  callback?(data: string): void;
}

type ChildHandle = {
  get: () => Terminal;
};

const Child: ForwardRefRenderFunction<ChildHandle, ChildProps> = (
  props,
  forRef
) => {
  const { callback } = props;

  const terminal = new Terminal();

  // The ref where the terminal will be stored and returned
  const ref = useRef<HTMLDivElement>(null);

  useImperativeHandle(forRef, () => ({
    get() {
      return terminal;
    },
  }));

  useLayoutEffect(() => {
    // Calls whenever terminal is typed into
    if (callback) terminal.onData(callback);

    // Mount terminal to the DOM
    if (ref.current) terminal.open(ref.current);
  }, []);

  return <div ref={ref}></div>;
};

export default forwardRef(Child);