Как работает механизм реагирующих зацепок? - PullRequest
0 голосов
/ 22 декабря 2018

Примечание. Этот вопрос не о написании пользовательских хуков в React .

Недавние разработки в React позволяют нам создавать хуки, т.е.для состояния React, внутри такой простой функции, как:

function App () {
  const [someVar, setSomeVar] = useState('someVarDefaultValue');
  return (
    <div 
      onClick={() => setSomeVar('newValue')}>{someVar}
    </div>
  );
}

Хук useState возвращает массив с аксессором и мутатором, и мы используем их декомпозицией массива внутри нашей функции App.

Так что под капотом ловушка выглядит примерно так (просто псевдокод):

function useState(defaultValue) {
  let value = defaultValue;

  function setValue(val) {
    value = val;
  }

  return [value, setValue];
}

Когда вы попробуете этот подход в JS, он не будет работать - значение, разложенное из массива, не будет обновляться, если вы используетеsetValue где-то.Даже если вы используете value как объект, а не примитив defaultValue.

У меня вопрос, как работает механизм ловушек в JS?

Из того, что я видел в React исходный код он использует функцию редуктора и проверку типа с помощью Flow.Чтобы понять общую картину, мне сложно следовать коду.

1 Ответ

0 голосов
/ 22 декабря 2018

Вы должны сохранить значение вне функции, чтобы оно возвращало постоянные результаты при вызовах.Кроме того, установка значения должна вызывать повторное рендеринг компонента, в который он вызывается:

 // useState must have a reference to the component it was called in:
 let context;

 function useState(defaultValue) {
   // Calling useState outside of a component won't work as it needs the context:
   if(!context) throw new Error("Can only be called inside render");
   // Only initialize the context if it wasn't rendered yet (otherwise it would re set the value on a rerender)
   if(!context.value)
    context.value = defaultValue;
   // Memoize the context to be accessed in setValue
   let memoizedContext = context;
   function setValue(val) {
      memoizedContext.value = val;
      // Rerender, so that calling useState will return the new value
      internalRender(memoizedContext);
   }

  return [context.value, setValue];
 }

// A very simplified React mounting logic:
function internalRender(component) {
   context = component;
   component.render();
   context = null;
}



 // A very simplified component
 var component = {
  render() {
    const [value, update] = useState("it");
    console.log(value);
    setTimeout(update, 1000, "works!");
  }
};

internalRender(component);

Затем, когда вызывается setValue, компонент повторяется, useState будет вызван снова, и новое значениебудет возвращен.

...