Я хотел бы передать оба состояния в результате метода getSettings в компоненте настроек для добавления компонента. Есть ли способ сделать это, не копируя метод в другой компонент?
Спасибо
Компонент настроек
export class Settings extends Component {
constructor(props) {
super(props);
this.state = {
languages: [],
currencies: []
};
}
getSettings() {
fetch('/settings', {
method: 'get',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then( ... )
.then(json => {
this.setState({
currencies: json.currencies,
languages: json.languages
});
})
}
}
Добавить компонент
export class Add extends Component {
displayName = Add.name
constructor(props) {
super(props);
this.state = {
languages: [],
currencies: []
};
}





Вы можете попробовать что-то вроде ниже, не зная, как и где вы визуализируете компонент <Add>:
export class Settings extends Component {
constructor(props) {
super(props);
this.state = {
languages: [],
currencies: []
};
}
getSettings() {
return fetch('/settings', {
method: 'get',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(...)
.then(json => {
this.setState({
currencies: json.currencies,
languages: json.languages
});
})
}
render() {
return (
{ this.getSettings() && <Add { ...this.state}/> }
);
}
}В вашем компоненте <Add> просто прочтите из props в его методе рендеринга.
В этом случае хороша компонента высокого порядка. Сначала вы создаете метод withSetting.
export const withSetting = (Component) => class extends Component {
constructor(props) {
super(props);
this.state = {
languages: [],
currencies: []
};
}
getSettings() {
fetch('/settings', {
method: 'get',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then( ...)
.then(json => {
this.setState({
currencies: json.currencies,
languages: json.languages
});
})
}
render() {
return <Component getSetting = {this.getSetting} {...this.state} />
}
}
И в Add Component. Вы можете вызвать в this.props.getSetting или использовать this.props. languages и this.props. currencies
import {withGetSetting} from './withGetSetting'
class Add extends React.Component {...}
export default withSetting(Add)
О HOC можно прочитать здесь https://reactjs.org/docs/higher-order-components.html
Я могу придумать два способа поделиться своими состояниями в Settings.
вы можете просто передать состояние в this.props.children или this.props.render.
class Settings extends Component {
state = {
languages: [],
currencies: []
};
getSettings = () => {
// fetch('/settings', {
// method: 'get',
// headers: {
// 'Accept': 'application/json',
// 'Content-Type': 'application/json'
// }
// })
// .then( ... )
// .then(json => {
// this.setState({
// currencies: json.currencies,
// languages: json.languages
// });
// })
this.setState({
currencies: ["Dollar", "Euro", "Won"],
languages: ["English", "French", "Korean"]
});
};
componentDidMount() {
this.getSettings();
}
render() {
return <div>{this.props.children(this.state)}</div>;
}
}
И используйте это вот так.
const renderComponents = (currencies, languages) => {
const currencyItems = currencies.map(currency => (
<li key = {currency}>{currency}</li>
));
const languageItems = languages.map(language => (
<li key = {language}>{language}</li>
));
return (
<div>
<h3>currencies</h3>
<ul>{currencyItems}</ul>
<hr />
<h3>languages</h3>
<ul>{languageItems}</ul>
</div>
);
};
const AppChildAsFunction = () => (
<div>
<h2>Using Child as Function</h2>
<Settings>
{({ currencies, languages }) => renderComponents(currencies, languages)}
</Settings>
</div>
);
Создание и экспорт нового поставщика настроек и потребителя
SettingsContext.js
import React, { Component } from "react";
const defaultValue = {
currencies: ["Dollar", "Euro", "Won"],
languages: ["English", "French", "Korean"]
};
const { Provider, Consumer: SettingsConsumer } = React.createContext(
defaultValue
);
class SettingsProvider extends Component {
state = {
languages: [],
currencies: []
};
render() {
return <Provider value = {this.state}>{this.props.children}</Provider>;
}
}
export { SettingsProvider, SettingsConsumer };
И использование примерно такое же
App.js
import { SettingsProvider, SettingsConsumer } from "./SettingsContext";
...
const AppWithContext = () => (
<div>
<h2>Using Context API</h2>
<SettingsConsumer>
{({ currencies, languages }) => renderComponents(currencies, languages)}
</SettingsConsumer>
</div>
);
Вы можете просто выбрать тот, который вам больше по вкусу.
Извините за мою страстную мольбу, но из любви ко всему священному, пожалуйста, не используйте абстракции компонентов более высокого порядка, рендеринг реквизитов и т. д. Это простая проблема, которая заслуживает простого решения. Никакой магии не требуется. Никаких эзотерических знаний React не требуется.
Поздравляем, теперь у вас есть многоразовая и независимо тестируемая функция, которую любой компонент (или что-то еще в этом отношении) может использовать по мере необходимости.
const getSettings = component => () => {
// Do your fetch here, and in your last
// .then(), just use 'component.setState'
// instead of 'this.setState'
// Simulate async
setTimeout(() => {
component.setState({
name: component.name,
currencies: "whatever",
languages: "yep"
});
}, 1000);
};
export default getSettings;
Это так просто.
См. https://codesandbox.io/s/mjmpw5k08y
Оглядываясь назад, вы, вероятно, могли бы принять функцию обратного вызова вместо компонента или вернуть обещание. Любой из них сделает это менее связанным с компонентами React. Непроверенный код следует ...
const getSettings = callback => {
// Simulate async
setTimeout(() => {
callback({
name: component.name,
currencies: "whatever",
languages: "yep"
});
}, 1000);
};
Применение
getSettings(this.setState.bind(this));
const getSettings = () => window.fetch
.then(res => res.json());
Применение
getSettings().then(this.setState.bind(this));
@LambrosSymeonaki Simple - лучшее. Люблю это ?
@SungKim, твой ответ и этот чертовски полезны! Большое спасибо. И я говорил, что, может быть, Redux - подходящий способ! Я должен больше учиться.