Я хотел бы передать состояние брату, сестре или даже бабушке или дедушке кому угодно.
У меня 3 компонента. Внутри Header у меня есть кнопка с функцией onClick для переключения раскрывающегося меню внутри Navigation. И, кстати, хотелось бы передать такое же состояние на AnotherComponent.
Как передать состояние (например, isDropdownOpened) от Header к Navigation и AnotherComponent?
<div>
<Header />
<Navigation />
<div>
<div>
<div>
<AnotherComponent />
</div>
</div>
</div>
</div>



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


How to pass state (such as
isDropdownOpened) fromHeadertoNavigationandAnotherComponent, please ?
Вы храните состояние в предке Header и передаете это состояние в Haeader, Navigation и AnotherComponent в качестве свойств. См. Состояние и жизненный цикл и Поднятие состояния вверх в документации.
Пример:
const Header = props => (
<div>
<span>Header: </span>
{props.isDropdownOpened ? "Open" : "Closed"}
</div>
);
const Navigation = props => (
<div>
<span>Navigation: </span>
{props.isDropdownOpened ? "Open" : "Closed"}
</div>
);
const AnotherComponent = props => (
<div>
<span>AnotherComponent: </span>
{props.isDropdownOpened ? "Open" : "Closed"}
</div>
);
class Wrapper extends React.Component {
constructor(props) {
super(props);
this.state = {
isDropdownOpened: false
};
}
componentDidMount() {
setInterval(() => {
this.setState(({isDropdownOpened}) => {
isDropdownOpened = !isDropdownOpened;
return {isDropdownOpened};
});
}, 1200);
}
render() {
const {isDropdownOpened} = this.state;
return (
<div>
<Header isDropdownOpened = {isDropdownOpened} />
<Navigation isDropdownOpened = {isDropdownOpened} />
<div>
<div>
<div>
<AnotherComponent isDropdownOpened = {isDropdownOpened} />
</div>
</div>
</div>
</div>
);
}
}
ReactDOM.render(
<Wrapper />,
document.getElementById("root")
);<div id = "root"></div>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>Есть и другие опции, которые Арно полезно предоставляет в его ответ.
Спасибо, поднять состояние - это сложно, не так ли? Точно так же мы формируем ввод и форму?
@Steffi - это один из способов обеспечить единый источник правды для этой информации. если есть другая информация, которую вам нужно хранить таким же образом, вы можете сделать их обоими свойствами объекта, которым вы делитесь через дерево. Тем не менее, смотрите Ответ Арно для получения дополнительных опций. Вы не первый, кто сочтет это громоздким.
У вас есть разные подходы к решению этой ситуации.
Я пробовал React Context, но он не работает, когда это не то же дерево, верно?
Точно. Контекст предназначен для обмена данными, которые можно считать «глобальными» для дерева компонентов React. Если вам нужен глобальный магазин, вы можете рассмотреть возможность использования Redux.
Например, как сказал TJ, используйте состояние родительского компонента. Таким образом, все подкомпоненты разделяют одно состояние, что, как я полагаю, вы и хотели.
class ExampleParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
isDropdownOpened: false
}
}
toggleDropdown() {
this.setState({
isDropdownOpened: !isDropdownOpened
});
}
render() {
return (
<div>
<Header open = {isDropdownOpened} toggleDropdown = { this.toggleDropdown }/>
<Navigation open = { isDropdownOpened}/>
<div>
<div>
<div>
<AnotherComponent open = { isDropdownOpened} />
</div>
</div>
</div>
</div>
);
}
class Header extends React.Component {
render() {
return (
<div>
<button onClick = { this.props.toggleDropdown }>TOGGLE ME</button>
{ isDropdownOpened && (
<h1> DROPPED </h1>
}
</div>
);
}
}
Я не понимаю эту часть кода: <button onClick = { this.props.toggleDropdown } /> - <Header toggleDropdown = { this.toggleDropdown } />, затем toggleDropdown() { this.setState({ isDropdownOpened: !isDropdownOpened }); }. Значит, функция onclick для переключения исходит от родителя?
при нажатии на кнопку он вызывает props.toggleDropdown, который указывает на функцию, которая переключает isDropdownOpened, при изменении этого значения новое переключаемое значение детализируется до всех компонентов, которые подписываются (?) на это состояние.
Да, переключатель исходит от родителя и распространяется на всех дочерних элементов. Вы даже можете сохранить значение в компоненте Header, но чтобы отправить его братьям и сестрам, вам все равно придется создать состояние в ParentComponent и передать значение остальным дочерним элементам. Если мы сделаем это таким образом, не будет единого источника истины, поэтому я передал его Родителю и распространил на всех детей. Но это очень самоуверенный стиль кодирования.
Это точная причина, по которой были разработаны (и раскручены сообществом ?) "Реагировать на хуки", но не используйте их еще в производстве, они все еще находятся на ранней стадии разработки (альфа), и их спецификация / реализация могут быть изменены!
Проблему можно решить с помощью замечательного API «Реагировать на контекст», который позволяет передавать данные компонентам независимо от того, насколько глубоко они вложены в дерево. Чтобы познакомиться с контекстом, прочтите обширную документацию по ссылке выше. Я объясню здесь лишь небольшой и быстрый пример:
App.jsx
import React from "react";
// The initial value can be anything, e.g. primitives, object, function,
// components, whatever...
// Note that this is not required, but prevebents errors and can be used as
// fallback value.
const MyContext = React.createContext("anything");
// This component is the so called "consumer" that'll provide the values passed
// to the context component. This is not necessary, but simplifies the usage and
// hides the underlying implementation.
const MyContextConsumer = MyContext.Consumer;
const someData = { title: "Hello World" };
const App = ({ children }) => (
<MyContext.Provider value = {someData}>{children}</MyContext.Provider>
);
export { MyContextConsumer };
export default App;
AnotherComponent.jsx
import React from "react";
import { MyContextConsumer } from "./App";
const AnotherComponent = () => (
<div>
<MyContextConsumer>{({ title }) => <h1>{title}</h1>}</MyContextConsumer>
</div>
);
export default AnotherComponent;
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import AnotherComponent from "./AnotherComponent";
const Root = () => (
<App>
<AnotherComponent />
</App>
);
const rootElement = document.getElementById("root");
ReactDOM.render(<Root />, rootElement);
Компонент отобразит заголовок уровня 1 с текстом «Hello World».
Спасибо!!! Я предпочитаю это решение, но какое будет решение с использованием хуков? Ты можешь это сделать?
Я пробовал это, но это не работает ... Это не то же самое дерево.
to <MyContextConsumer> всегда должен находиться внутри <MyContextProvider>, верно?
Я расширил пример кода и добавил Codesandbox (MVCE). Да, компонент Consumer всегда должен быть заключен в оболочку компонента Provider.
app и anotherComponent являются родительскими и дочерними, а не братьями и сестрами.
First Send the data from the first child to the common parent using callback
function and then send that received data (stored in state in parent component)
to the second child as props.
вы также можете прочитать эту статью - https://www.pluralsight.com/guides/react-communicating-between-components
Вы можете использовать только this.state.variableName для доступа
<ChildComponent data = {this.state.name} />
И передать функции
<ChildComponent data = {this.HandleChange} />
Поднимите состояние до общего предка закрытия и передайте состояние через props. Например, родительский компонент
Header, Navigation and AnotherComponent.