У меня есть компонент (PropertiesList.tsx)
, который показывает список свойств, поступающих из магазина (приставка), затем я хочу реализовать кнопку с функцией удаления, которая при нажатии удаляет это свойство из базы данных (эта часть работает) и из хранилища, поэтому он автоматически исчезает из представления без перезагрузки, проблема в том, что дочерний компонент (OnePropertyDetails.tsx)
, который отображается при щелчке по любому компоненту из списка, пытается выполнить рендеринг и, поскольку он был удален с помощью редуктора он выдает ошибку, так как не может ее найти. У меня в родительском состоянии есть состояние showdetails, и когда я go для функции удаления, я устанавливаю showdetails в false, так что компонент исчезает, так как он не должен отображать его, проблем не должно быть, но, видимо, this.setState({detailsVisible:false})
в строке 61 PropertiesList.tsx
ничего не делает или иным образом я не знаю, что не так, поскольку у дочернего компонента есть условие, такое как detailsVisible ? OnePropertyDetals : null
, и это работало для меня до
, это PropertiesList .tsx:
import React, { PureComponent, Component } from "react";
import "./style.css";
import {} from "reactstrap";
import { connect } from "react-redux";
import { IStore } from "../../interfaces/IStore";
import { IProperty, IProperties } from "../../interfaces/IProperties";
import { setPropertiesAction, deletePropertyAction } from "../../Redux/actions";
import { getProperties, deleteProperty } from "../../utils/API";
import { IProfile } from "../../interfaces/IProfile";
import OnePropertyDetails from "../OnePropertyDetails/OnePropertyDetails";
//own props
interface IProps {}
//props coming from the store
interface IGlobalProps {
//para coger el token me lo traigo de props
userProfile: IProfile;
//properties esta en el store y se lo pasamos al componente por props
properties: IProperties;
//el action hay que pasarlo por props(le paso como argumento lo mismo que he definido en el action)
setProperties(propertiesList: IProperty[]): void;
deletePropertyAct(id: number): void;
}
type TProps = IProps & IGlobalProps; //conjunto de las props propias y las que vienen del store
interface IState {
detailsVisible: boolean;
selectedIdProperty: number;
}
class PropertiesLlist extends Component<TProps, IState> {
constructor(props: TProps) {
super(props);
this.state = {
detailsVisible: false,
selectedIdProperty: null
};
this.showProperty = this.showProperty.bind(this);
this.deleteOneProperty = this.deleteOneProperty.bind(this);
}
componentDidMount() {
(async () => {
const token = localStorage.getItem("token");
const response = await getProperties(token);
this.props.setProperties(response);
})();
}
showProperty(id: number): any {
this.setState({ selectedIdProperty: id, detailsVisible: true });
}
//TODO borrar si funciona desde el hijo
deleteOneProperty(id: number) {
this.setState({ detailsVisible: false });
console.log(this.state.detailsVisible);
const token = localStorage.getItem("token");
deleteProperty(token, id); //fetch
this.props.deletePropertyAct(id); //action
}
render() {
const { byId } = this.props.properties;
const { detailsVisible } = this.state;
const properties = Object.values(byId); // it creates an array out of the object(byId)
return (
<div>
<div
style={{ display: "flex", flexDirection: "row", flexWrap: "wrap" }}
>
{properties.map(property => (
<div
key={property.id_property}
className="card border-light mb-3 ml-2 "
style={{ maxWidth: "14rem", cursor: "pointer" }}
onClick={() => this.showProperty(property.id_property)}
>
<img
src="https://images.adsttc.com/media/images/5d34/e507/284d/d109/5600/0240/newsletter/_FI.jpg?1563747560"
className="card-img-top"
alt="..."
></img>
<div className="card-header">
<div className="card-body">
<p style={{ fontSize: 14 }} className="card-title">
{property.address_line1}
</p>
</div>
<button
onClick={() => this.deleteOneProperty(property.id_property)}
>
Eliminar
</button>
</div>
</div>
))}
</div>
<div className="offset-3 col-md-6">
{" "}
{detailsVisible ? (
<OnePropertyDetails id={this.state.selectedIdProperty} />
) : null}
</div>
</div>
);
}
}
//states we are gonna take from the IStore
const mapStateToProps = (store: IStore) => ({
userProfile: store.userProfile,
properties: store.properties
});
// we send actions to the reducer(only to save states) and the reducer save what we send in the action
const mapDispatchToProps = {
setProperties: setPropertiesAction,
deletePropertyAct: deletePropertyAction
};
export default connect(mapStateToProps, mapDispatchToProps)(PropertiesLlist);
Это дочерний компонент OnePropertyDetails.tsx:
import React, { PureComponent } from "react";
import {} from "reactstrap";
import { IStore } from "../../interfaces/IStore";
import { connect } from "react-redux";
import { IProperties, IProperty } from "../../interfaces/IProperties";
import UpdateProperty from "../UpdateProperty/UpdateProperty";
import { deletePropertyAction, setPropertiesAction } from "../../Redux/actions";
import { deleteProperty, getProperties } from "../../utils/API";
//own props
interface IProps {
id: number;
}
//stor props
interface IGlobalProps {
properties: IProperties;
// deletePropertyAct(id: number): void;
}
type TProps = IProps & IGlobalProps;
interface IState {
formVisible: boolean;
}
class OnePropertyDetails extends PureComponent<TProps, IState> {
constructor(props: TProps) {
super(props);
this.state = {
formVisible: false
};
this.showUpdateForm = this.showUpdateForm.bind(this);
// this.deleteOneProperty = this.deleteOneProperty.bind(this);
}
// deleteOneProperty() {
// (async () => {
// try {
// const token = localStorage.getItem("token");
// await deleteProperty(token, this.props.id); //fetch
// this.props.deletePropertyAct(this.props.id); //action
// console.log(this.props.properties)
// } catch (error) {
// console.log(error)
// }
// })();
// }
showUpdateForm() {
console.log(this.state.formVisible); //TODO mirar con esther
if (this.state.formVisible === false) {
this.setState({
formVisible: true
});
} else {
this.setState({
formVisible: false
});
}
console.log(this.state.formVisible);
}
render() {
//TODO mostrar las incidencias de cada componente
const { id } = this.props;
const property = this.props.properties.byId[id];
return (
<div>
<div
className="card border-light mb-3"
style={{ maxWidth: "20rem", cursor: "pointer" }}
>
<img src="..." className="card-img-top" alt="..."></img>
<div className="card-header">
<div className="card-body">
<h5 className="card-title">{property.address_line1}</h5>
<h6 className="card-title">{property.address_line2}</h6>
<p style={{ marginLeft: "25%" }} className="card-text">
{property.locality}
</p>
<p style={{ marginLeft: "15%" }}>
{property.region}-- {property.postcode}
</p>
</div>
</div>
<button
style={{ marginLeft: "5%" }}
type="submit"
className="btn btn-sm btn-outline-secondary mt-4"
data-target="#updateProperty"
data-toggle="modal"
onClick={this.showUpdateForm}
>
Editar
</button>
<button
style={{ marginLeft: "5%" }}
type="submit"
className="btn btn-sm btn-outline-secondary mt-4"
// data-target="#updateProperty"
// data-toggle="modal"
// onClick={this.deleteOneProperty}
>
Eliminar
</button>
</div>
<div
className="modal fade"
id="updateProperty"
role="dialog"
aria-labelledby="exampleModalLabel"
aria-hidden="true"
>
<div className="modal-dialog" role="document">
<div className="modal-content">
<div className="modal-header">
<h5 className="modal-title" id="exampleModalLabel">
Actualizar Propiedad {id}
</h5>
<button
type="button"
className="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">×</span>
</button>
</div>
{this.state.formVisible ? (
<div className="modal-body">
<UpdateProperty
propertyId={id}
desmontar={this.showUpdateForm}
/>
</div>
) : null}
<div className="modal-footer"></div>
</div>
</div>
</div>
</div>
);
}
}
const mapStateToProps = (store: IStore) => ({
properties: store.properties
});
const mapDispatchToProps = {
// deletePropertyAct: deletePropertyAction,
};
export default connect(mapStateToProps, mapDispatchToProps)(OnePropertyDetails);
редуктор:
import { IPropertyForm } from "./../../interfaces/IPropertyForm";
import { TAction } from "../types";
import produce from "immer";
import { IProperties } from "../../interfaces/IProperties";
const initialState: IProperties = {
byId: {},
order: []
};
export default (state: IProperties = initialState, action: TAction) =>
produce(state, draftState => {
switch (action.type) {
case "SET_PROPERTIES":
const properties = action.payload;
draftState.byId = {}; // inic para no duplicar, ojo si quieres mantener algún dato
draftState.order = [];
properties.forEach(property => {
draftState.byId[property.id_property] = property;
draftState.order.push(property.id_property);
});
break;
case "UPDATE_PROPERTY":
draftState.byId[action.payload.id_property] = action.payload;
break;
case "DELETE_PROPERTY":
console.log("entro al reducer",action.payload);
delete draftState.byId[action.payload];
const pos = draftState.order.indexOf(action.payload);
draftState.order.splice(pos, 1);
console.log("acaba el reducer");
break;
case "SET_LOGOUT":
return (state = initialState);
default:
return state;
}
});
и вывод:
OnePropertyDetails.tsx:78 Uncaught TypeError: Cannot read property 'address_line1' of undefined
at OnePropertyDetails.render (OnePropertyDetails.tsx:78)
at finishClassComponent (react-dom.development.js:18470)
at updateClassComponent (react-dom.development.js:18423)
at beginWork$1 (react-dom.development.js:20186)
at HTMLUnknownElement.callCallback (react-dom.development.js:336)
at Object.invokeGuardedCallbackDev (react-dom.development.js:385)
at invokeGuardedCallback (react-dom.development.js:440)
at beginWork$$1 (react-dom.development.js:25780)
at performUnitOfWork (react-dom.development.js:24695)
at workLoopSync (react-dom.development.js:24671)
at performSyncWorkOnRoot (react-dom.development.js:24270)
at react-dom.development.js:12199
at unstable_runWithPriority (scheduler.development.js:697)
at runWithPriority$2 (react-dom.development.js:12149)
at flushSyncCallbackQueueImpl (react-dom.development.js:12194)
at flushSyncCallbackQueue (react-dom.development.js:12182)
at discreteUpdates$1 (react-dom.development.js:24423)
at discreteUpdates (react-dom.development.js:1438)
at dispatchDiscreteEvent (react-dom.development.js:5881)