В React, как я могу вызвать функцию в одном компоненте (которая отображается с помощью маршрутизатора React) из другого компонента?

#javascript #reactjs

Вопрос:

Мне действительно не помешала бы помощь с этим блокпостом. У меня есть довольно простое приложение React, которое я создаю, которое имеет следующие компоненты и структуру:

 App
   Nav
   Main
      PageOne
          PageOneDetail
      ...
 

Я хотел бы иметь возможность вызывать функцию в Nav из любого из компонентов в структуре компонентов. Насколько я понимаю, до сих пор я мог бы сделать это с props помощью . Я знаю, как я мог бы сделать это с помощью дочернего компонента, но не уверен, как этого добиться с помощью компонентов внуков или братьев.

Я пытаюсь придерживаться функциональных компонентов и использовать vanilla React, и я использую маршрутизатор React для визуализации компонентов с маршрутом.

Вот упрощенная версия кода, который у меня есть:

App.js

 import React, { Component } from 'react'
import Nav from './Nav'
import Main from './Main'


class App extends Component {

    render() {
        <Nav />
        <Main />
    }
}

export default App
 

Nav.js

 import React from 'react'
import { NavLink } from 'react-router-dom'

const Nav = (props) => {
    
    function doSomethingToNav() {

         // here's where I want to change appearance or do whatever to the nav
         // I want to be able to use this function from within my Nav component
         // but also want to be able to call it from any another component
    }

    return(
        <nav>
            <NavLink to="/pageone">Page One</NavLink>
            <NavLink to="/pagetwo">Page Two</NavLink>
        </nav>
    )
}

export default Nav
 

Main.js

 import React from 'react'
import { Route, Switch } from 'react-router-dom'
import PageOne from './PageOne'
import PageOne from './PageTwo'

const Main = (props) => {

    return (
            
    <main>
        <Switch>
            <Route exact path='/pageone'><PageOne /></Route>
            <Route exact path='/pagetwo'><PageTwo /></Route>
        </Switch>
    </main>
}   

export default Main
 

PageOne.js

 import React, { Component } from 'react'
import { Switch, Route, BrowserRouter } from 'react-router-dom'
import SomeListOfItems from 'SomeListOfItems'

export default class PageOne extends Component {

    render(){
        <BrowserRouter>
            <Switch>
                <Route>
                    <SomeListOfItems />
                </Route>
                <Route path="/pageone/:number/" component={PageOneItem} />
                // I also want to call the doSomethingToNav method when I route to the 
                // PageOneItem component

            </Switch>
        </BrowserRouter>
    }
}

 

PageOneItem.js

 import React from 'react'
import { Link } from 'react-router-dom'


const PageOneDetail = (props) => {
    
    return(
        <div>
            <Link to='/pageone'>Close</Link> 
            // I want to call doSomethingToNav function when I close this component. 
            // Close, in this case, is just routing back to /pageone
            <p>Some content...</p>
        </div>
    )
}

export default PageOneDetail

 

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

1. Поскольку и навигационная, и основная являются родными братьями и сестрами , вы можете либо перенести эту функцию на свой App.js так что вы можете передать эту функцию в качестве реквизита или использовать контекст .

2. если вы не собираетесь использовать какие-либо реквизиты, переданные в Nav компоненте внутри doSomethingToNav , я бы рекомендовал переместить функцию в отдельный файл и импортировать ее туда, куда вам нужно

3. для вызова любой функции при «закрытии» (размонтировании) любого компонента React.useEffect(() => {return () => doSomethingToNav()}) и при «маршруте» к любому маршруту (монтированию) используйте React.useEffect(() => {doSomethingToNav()}, [])

4. Спасибо всем. Сочетание ваших комментариев и ответа Марио ниже помогло мне в этом разобраться.

Ответ №1:

Вы могли бы пойти с решением, чтобы иметь контекст и иметь все обернутые компоненты для доступа к нему с useContext помощью крючка.

Вот как вы можете его использовать:

Компонент приложения

 import React, { Component, useState } from 'react';
import Nav from './Nav';
import Main from './Main';

const AppContext = createContext({
  executeNavigationFunction: () => {},
  setExecuteNavigationFunction: (navFunction: () => void) => {},
});
const App = () => {
  const [executeNavigationFunction, setExecuteNavigationFunction] = useState(() => {});

  return (
    <AppContext.Provider value={{ executeNavigationFunction, setExecuteNavigationFunction }}>
      <Nav />
      <Main />
    </AppContext.Provider>
  );
};

export default App;
 

Навигационный компонент

 import React, {useEffect, useContext, useEffect} from 'react';
import { NavLink } from 'react-router-dom';
import AppContext from 'app';

const Nav = (props) => {
  const { setExecuteNavigationFunction } = useContext(AppContext);

  function doSomethingToNav() {
    // here's where I want to change appearance or do whatever to the nav
    // I want to be able to use this function from within my Nav component
    // but also want to be able to call it from any another component
  }

  useEffect(() => {
    setExecuteNavigationFunction(doSomethingToNav)
  }, [setExecuteNavigationFunction])

  return (
    <nav>
      <NavLink to="/pageone">Page One</NavLink>
      <NavLink to="/pagetwo">Page Two</NavLink>
    </nav>
  );
};

export default Nav;
 

Компонент PageOneItem

 import React, {useEffect, useContext} from 'react';
import { Link } from 'react-router-dom';
import AppContext from 'app';

const PageOneDetail = (props) => {
  const { executeNavigationFunction } = useContext(AppContext);

  useEffect(() => {
    executeNavigationFunction();
  }, []);
  return (
    <div>
      <Link to="/pageone">Close</Link>
      // I want to call doSomethingToNav function when I close this component. // Close, in this case, is just routing
      back to /pageone
      <p>Some content...</p>
    </div>
  );
};

export default PageOneDetail;
 

Таким образом, вы можете использовать любые данные из любой части приложения в любой части приложения.

Это может показаться немного банальным, но если вы не используете какую-либо глобальную библиотеку состояний, контекст-это правильный путь.

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

1. Большое тебе спасибо, Марио! Я пошел по этому пути с несколько иной реализацией. Я обнаружил, что попытка создать контекст с помощью метода немного слишком сложна. Вместо этого я просто использовал контекст, чтобы отслеживать состояние навигации. И в зависимости от того, какой компонент загружен, я бы соответственно изменил состояние.

2. Без проблем. Рад, что подтолкнул тебя в правильном направлении. Да, вы можете пойти по этому пути лучше, но это кажется слишком сложным, чтобы установить метод и передать его обратно в контекст.