#javascript #reactjs #request #observer-pattern
Вопрос:
Поэтому я прочитал эту документацию о компонентах более высокого порядка и подумал, что было бы неплохо провести рефакторинг моего ужасного компонента monolit.
Мой план:
- Подпишитесь на наблюдаемую ценность
- Извлеките данные и сохраните их на карте
- Уведомляйте об изменениях и каким-то образом обновляйте пользовательский интерфейс (я новичок в react и знаю, что есть много способов redux, mobx и т. Д.). Но цель моего маленького проекта-учиться, и я хочу, чтобы все было как можно более родным (за исключением библиотек пользовательского интерфейса).
Любой приведенный ниже код может быть неправильным, поэтому вот мой класс наблюдателя, который практически не изменился с 1994 года
export default class Observable { //observer
listeners = new Set();
constructor(value) {
this.value = value;
}
get() {
return this.value;
}
set(newValue) {
if (newValue !== this.value) {
this.notify();
}
}
subscribe(listener) {
this.listeners.add(listener);
}
unsubscribe(listener) {
this.listeners.delete(listener);
}
notify() {
for (const listener of this.listeners) {
listener();
}
}
}
А вот компонент более высокого порядка, который управляет подпиской моего наблюдаемого класса
import React from "react";
export default function withSubscription(WrappedComponent, api) {
return class extends React.Component {
constructor(props) {
super(props);
this.state = {
data: api.observableMapData //observable object
};
this.handleChange.bind(this);
}
handleChange() {
this.setState({
data: api.observableMapData
});
console.log("changed data") //never actually saw it in the console
}
componentDidMount() {
//observable object->getterfield->subscribe
api.fetchData();
api.observableMapData.dataSourceMap.subscribe(this.handleChange);
}
componentWillUnmount() {
//observable object->getterfield->unsubscribe
api.observableMapData.dataSourceMap.unsubscribe(this.handleChange);
}
render() {
return <WrappedComponent data={this.state.data} {...this.props} />;
}
};
}
the observed class
import Observable from "../services/Observable";
export default class DataSourceMap {
_dataSources = [
["projects", "Project"],
["organizations", "Organization"],
];
_dataSourceMap = new Observable(new Map());
get dataSourceMap() {
return this._dataSourceMap;
}
set dataSourceMap(val) {
this._dataSourceMap = val;
}
set dataSources (sources) {
this._dataSources = sources;
}
get dataSources () {
return this._dataSources ;
}
}
And the wrapper function
const api = new Api();
const TicketFormWithSubscription = withSubscription(
UiFormController,
api
);
export default TicketFormWithSubscription;
And I am fetching the map like that
export default class ApiService {
observableMapData = new DataSourceMap();
//singleton
constructor() {
if (!ApiService._instance) {
ApiService._instance = this;
// this.fetchData();
}
return ApiService._instance;
}
async fetchData() {
let tmpMap = new Map();
for (let value of this.observableMapData.dataSources) {
let data = await this.getAllItems(value[1]);
console.log(data);
tmpMap.set(value[1], data)
}
//setter
this.observableMapData.setDataSourceMap = tmpMap;
console.log(JSON.stringify(this.observableMapData " fetched"));
}
...
}
So the main problem is that component loads faster then data is retrieved. And when I open my Autocomplete there is an error because values are undefined and dont update after the data is fetched.
Наконец, вот поток реквизита
- В UIconteroller (события handels)
constructor(props) {
super(props);
this.state = {
observableMap: this.props.data,
confimOpen: false,
....
}
...
render() {
const newProps = {
projects : this.props.data.dataSourceMap.get('projects'),
organizations: this.props.data.dataSourceMap.get('organizations'),
}
return (
<TicketFormUi
{...newProps}
/>
);
}
И к компоненту автозаполнения MaterialUI
<Autocomplete
options={props.organizations.items}
margin="dense"
getOptionLabel={(option) => `${option.name}`}
renderInput={(params) => (
...
<Autocomplete
options={props.projects.items}
margin="dense"
getOptionLabel={(option) => `${option.name}`}
renderInput={(params) => (
...
Я чувствую, что мой подход устарел, но выполним, заранее спасибо за свежий взгляд
Ответ №1:
nvm, который я получил, будет просто использовать крючки или acync componentDidMount()