#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. Без проблем. Рад, что подтолкнул тебя в правильном направлении. Да, вы можете пойти по этому пути лучше, но это кажется слишком сложным, чтобы установить метод и передать его обратно в контекст.