Недавно столкнулся с ошибкой при использовании React + Mobx. Я знаю, что ошибка была устранена в mobx-react@6.0.0, но теперь я вижу ее снова. Я нашел способ обойти это, но я чувствую, что он делает слишком много компромиссов с точки зрения читабельности и структуры кода.
Вот что я вижу:
При использовании mobx я могу создать store
и пометьте некоторые элементы данных как @observable
, чтобы указать, что за ними нужно следить за изменениями. В соответствии с этими документами, я должен затем поместить @observer
и @inject(...)
в компонент, который читает эти значения, и все будет правильно восстановлено.
Например, мой магазин Patient.store.ts :
import { observable, action, computed } from 'mobx';
import patientsList from '../../dummy-data/patients.json';
interface Patient {
firstName: string;
lastName: string;
gender: string;
age: number;
id: string;
guid: string;
}
export class PatientsStore {
@observable patients: Patient[] = [];
@observable selectedPatient?: Patient;
constructor() {
this.loadPatients();
}
loadPatients() {
const res = patientsList.patients; // will come from an API when the API is ready
this.setPatients(res);
}
@action('SET_PATIENTS')
setPatients(data: Patient[]) {
this.patients = data;
}
@action('SELECT_PATIENT')
selectPatient(patientId: string) {
const selectedPatient = this.patients.find(patient => patient.id === patientId);
if (selectedPatient) {
this.selectedPatient = selectedPatient;
}
}
}
const patientsStore = new PatientsStore();
export default patientsStore; // Create a singleton
И мой компонент класса PatientsList.tsx :
@inject('patientsStore')
@observer
class PatientsList extends React.Component<PatientsListProps, PatientsListState> {
constructor(props: {}) {
super(props);
this.state = { collapsed: false };
}
getMenuItems = () => {
return this.props.patientsStore!.patients.map((patient: Patient) => {
return (
<Menu.Item key={patient.id}>
<Icon type={patient.gender === 'male' ? 'man' : 'woman'} />
<span className='patient-name'>
{patient.lastName}, {patient.firstName}
</span>
</Menu.Item>
);
});
};
render() {
const menuItems = this.getMenuItems();
return (
<Sider
breakpoint='md'
collapsedWidth='0'
style={{
overflow: 'auto',
height: '100vh',
position: 'fixed',
left: 0,
}}
>
<Menu mode='inline'>{menuItems}</Menu>
</Sider>
);
}
}
export default PatientsList;
Вопрос в том, когда я делаю это таким образом (используя этот компонент класса), В браузере появляется сообщение об ошибке Cannot read property componentWillReact of undefined
. Но, когда я переписываю компонент как забавный функциональный компонент, я не получаю ошибки, и он отображается так, как ожидалось:
import React from 'react';
import { Layout, Menu, Icon } from 'antd';
import { Patient } from '../interfaces/Patient';
import { inject, observer } from 'mobx-react';
import patientsStore, { PatientsStore } from '../stores/patients.store';
const { Sider } = Layout;
interface PatientsListProps {
patientsStore?: PatientsStore;
}
const handleClick = (patientId: string) => {
patientsStore.selectPatient(patientId);
};
const getMenuItems = (patients: Patient[]) => {
return patients.map((patient: Patient) => {
return (
<Menu.Item key={patient.id} onClick={() => handleClick(patient.id)}>
<Icon type={patient.gender === 'male' ? 'man' : 'woman'} />
<span className='patient-name'>
{patient.lastName}, {patient.firstName}
</span>
</Menu.Item>
);
});
};
export const PatientsList = inject('patientsStore')(
observer((props: PatientsListProps) => (
<Sider
breakpoint='md'
collapsedWidth='0'
style={{
overflow: 'auto',
height: '100vh',
position: 'fixed',
left: 0,
}}
>
<Menu mode='inline'>{getMenuItems(props.patientsStore!.patients)}</Menu>
</Sider>
))
);
Почему я вижу эту ошибку? Почему аннотации работают в магазине, а не в компоненте? Есть ли лучший способ написать компонент, который будет так же удобен для чтения, как и аннотации?
Соответствующие версии:
{
"mobx": "^5.14.0",
"mobx-react": "^6.1.3",
"react": "^16.9.0"
}