Как мой компонент useContext знает, как получить значение в App.js?

#javascript #reactjs #dom #react-hooks #use-context

Вопрос:

Я следую учебнику, и он выглядит так:

App.js

 import React from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import { Index } from './pages/index';
import { About } from './pages/about/about';
import { UserContext } from './UserContext';

function AppRouter() {
  return (
    <Router>
      <div>
    <nav>
      <ul>
        <li>
          <Link to='/'> Home</Link>
        </li>
        <li>
          <Link to='/about'>About</Link>
        </li>
      </ul>
    </nav>
    <UserContext.Provider value='hello from context'>
      <Route path='/' exact component={Index} />
      <Route path='/about/' component={About} />
    </UserContext.Provider>
      </div>
    </Router>
  );
}

export default AppRouter;

 

UserContext.js

 import {createContext} from 'react'



export const UserContext = createContext(null);

 

index.js

 import React, { useContext } from 'react';
import { UserContext } from '../UserContext';

export function Index() {
  const msg= useContext(UserContext);



  return (
    <div>
      <h2>Home</h2>;
      <div>{msg}</div>;
    </div>
  );
}

 

Когда я запускаю это приложение react, {msg} становится hello from context из App.js файла.

Чего я не понимаю, так это того, что, когда я смотрю на index.js то, что он импортируется из UserContext.js единственного, как он узнает, что файл value находится в App.js файле? На мой взгляд, это было бы видно только в том случае, если бы я также импортировал App.js файл. Но приложение react просто знает, где его найти. Является ли ответом то, что при index.js импорте UserContext приложение просматривает все, чтобы увидеть, откуда еще UserContext.js было импортировано, а затем строит вывод приложения react из всего, что оно видит? Я чувствую, что мне не хватает большой части приложений react, которые сделали бы все более понятным для меня.

Ответ №1:

Не поднимая завесы, которая является основой React, ReactDOM и Fiber, может быть проще всего рассматривать API контекста React как оптимизированную версию шаблона подъема состояния вверх.

Дело не в том, что React сканирует весь ваш импорт и тому подобное, чтобы определить, какие файлы используются, а скорее, это DOM (виртуальное пространство React), которое создается, когда вы составляете все свои компоненты для создания своего приложения. Вы используете JSX для описания структуры пользовательского интерфейса.

Мы начнем с примера подъема состояния вверх.

 const Child1 = ({ value }) => <div>{value}</div>;

const Child2 = ({ setValue }) => <button onClick={() => setValue(42)}>Update</button>;

const Parent = () => {
  const [value, setValue] = React.useState(0);
  return (
    <>
      <Child1 value={value} />
      <Child2 setValue={setValue} />
    </>
  );
};
 

Здесь у нас есть дерево с Parent корнем и двумя листовыми узлами, Child1 и Child2 . Обратите внимание, что Child1 он не знает или не заботится о том, откуда value он взялся или как он обновляется, он просто получает поддержку. Точно Child2 так же он не знает или не заботится о том, что потребляет значение, которое он обновляет.

Мы поднимем состояние на уровень выше и добавим промежуточный компонент.

 const IntermediateComponent1 = (props) => (
  <div>
    <h1>I'm an intermediate child component</h1>
    <Child1 value={props.value} />
  </div>
);

const IntermediateComponent2 = (props) => (
  <div>
    <h1>I'm another intermediate child component</h1>
    <Child2 setValue={props.setValue} />
  </div>
);

const Parent = () => {
  const [value, setValue] = React.useState(0);
  return (
    <>
      <IntermediateComponent1 value={value} />
      <IntermediateComponent2 setValue={setValue} />
    </>
  );
};
 

Теперь вы должны заметить две вещи:

  1. Дочерние элементы 1 и 2 находятся дальше друг от друга, но общая структура DOM позволяет данным поступать из одного места и использоваться дочерними элементами дальше по дереву.
  2. Промежуточные компоненты должны знать о реквизитах, которые они должны передать своим потомкам (проблема/шаблон, известный как «сверление реквизита»).

API контекста React-это способ сделать то же самое, но не нужно явно передавать (детализировать) реквизиты всем промежуточным дочерним элементам. Поставщик контекста-это корневой узел, предоставляющий значение, которое будет использоваться дочерними элементами ниже по дереву.

 const Child1 = () => {
  const { value } = useContext(MyContext); // <-- "value" out
  return <div>{value}</div>;
};

const Child2 = () => {
  const { setValue } = useContext(MyContext); // <-- "setValue" out
  return <button onClick={() => setValue(42)}>Update</button>
};

const IntermediateComponent1 = () => (
  <div>
    <h1>I'm an intermediate child component</h1>
    <Child1 />
  </div>
);

const IntermediateComponent2 = () => (
  <div>
    <h1>I'm another intermediate child component</h1>
    <Child2 />
  </div>
);

const Parent = () => {
  const [value, setValue] = React.useState(0);
  return (
    <MyContext.Provider value={{ value, setValue }}> // <-- "value" in
      <IntermediateComponent1 />
      <IntermediateComponent2 />
    </MyContext.Provider>
  );
};
 

Теперь, надеюсь, вы сможете увидеть, как значения, предоставляемые из контекста, могут быть использованы потомками с помощью сгенерированной древовидной структуры DOM. При использовании контекста дети получат доступ к значению контекста ближайшего поставщика контекста, расположенного над ними в дереве. Другими словами, компонент поставщика ближайшего предка.

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

1. Спасибо, я, кажется, не могу заставить работать первый блок кода, это в основном псевдокод, как будто я должен настроить крючок для определения значения SET? codesandbox.io/s/new?file=/src/App.js

2. @filifunk Это не псевдокод, хотя я явно не вдавался в подробности объявления контекста, я предполагал, что вы уже знали, как это сделать, и просто спрашивали, как потребляющие компоненты «знают», из какого контекста потреблять. В вашем codesandbox, похоже, нет никакого кода, кроме исходного кода шаблона, вы сохранили свои изменения?

3. @filifunk Пожалуйста, проверьте этот запущенный код и поле: codesandbox.io/s/…

4. @filifunk Всем компонентам React передается props объект, независимо от того, ссылаетесь вы на него или нет, зависит от того, передаются ли какие-либо реквизиты и требуется ли доступ к ним в дочерних компонентах. Вы также можете либо назвать props объект «реквизит» ( вы можете называть его как угодно, но «реквизит» — это условность ) const child1 = props => {.... и использовать props.propValue , либо вы можете использовать назначение const child1 = ({ propValue }) => {.... деструкции и получить доступ к propValue реквизиту. Когда я говорил о «получении реквизита», я хотел, чтобы внимание было сосредоточено на том факте, что мы явно передавали реквизит.

5. @filifunk Это было сделано для того, чтобы показать, как у вас есть источник истины в дереве реакций и почему/как передача реквизита может стать проблематичной, когда вам нужно начать сверление реквизита между исходным и потребляющим компонентами. С помощью контекстного API вам больше не нужно передавать реквизиты.