'In React-Redux,how do i get the status of a saga function call in reactjs component?
I am new to reactjs and react-redux. I've joined a project that's building a project from a reactjs with react-redux template. I have two tables through axios - places(id,city,state,country) and resorts(resortId,name,address,phone,details and placeId).Through axios api in redux, the api -api/resorts return these data -(resortId,name,address,phone,details,placeId,city,state,country) to be bound to my grid.
The grid has a modal dialog for add/edit in which it has city,state,country dropdowns. So, the city Id is the placeId foreign key in the resorts table which I'm sending back on the apis for add and edit. I'm also sending extra data on add/edit of resorts - city,state and country(though these 3 belong to place table) to the add/edit apis because the grid is bound from ComponentDidUpdate function where resorts is assigned this.props. But after the resort is successfully added or updated into the resort table,in the ComponentDidUpdate table in the this.props - the grid data for only resort being updated or added has these 3 missing - state,city and country but all the other resort data in this.props has all these state,city and country intact. This is happening despite the fact that i'm sending these 3 city,state and country(doesn't belong to resort table) in resort entity that's being sent to the add or edit apis. Do I call getResorts saga again or add these missing 3 in this.props after finding the resort by Id in componentDidUpdate?
ComponentDidUpdate is
componentDidUpdate(prevProps, prevState, snapshot) {
///I need to get the status from saga for add or edit successs here
/// and either call the getresorts all function again or add the missing 3 city,state and country
//this.props before its being assigned to { resorts } for the particatular resortId being added or edited
console.log(data);
const { resorts } = this.props;
if (!isEmpty(resorts) && size(prevProps.resorts) !== size(resorts)) {
this.setState({ resorts: {}, isEdit: false });
}
}
The code is as below
1)resort-list.js
import React, { Component} from "react";
import PropTypes, { number } from "prop-types";
import { connect } from "react-redux";
import MetaTags from "react-meta-tags";
import { withRouter, Link } from "react-router-dom";
import {
Card,
CardBody,
Col,
Container,
Row,
Modal,
Button,
ModalHeader,
ModalBody,
Label
} from "reactstrap";
import paginationFactory, {
PaginationProvider,
PaginationListStandalone,
} from "react-bootstrap-table2-paginator";
import ToolkitProvider, { Search } from "react-bootstrap-table2-toolkit";
import BootstrapTable from "react-bootstrap-table-next";
import images from "assets/images";
import { Formik, Field, Form, ErrorMessage } from "formik";
import * as Yup from "yup";
//Import Breadcrumb
import Breadcrumbs from "components/Common/Breadcrumb";
import DeleteModal from "components/Common/DeleteModal";
import resortsSaga from 'store/resorts/saga';
import { select } from 'redux-saga/effects';
import {
getResorts,
addNewResort,
updateResort,
deleteResort,
} from "store/resorts/actions";
import store from 'store/index';
import {
getPlaces,
getPlaceDetails
} from "store/places/actions";
import { isEmpty, size, map } from "lodash";
import { getFormInitialValues } from "redux-form";
class ResortsList extends Component {
constructor(props) {
super(props);
this.node = React.createRef();
this.state = {
placeId:number,
resorts: [],
places:[],
previousPlace: {},
resort: "",
modal: false,
deleteModal: false,
resortsListColumns: [
{
text: "#ID",
dataField: "id",
sort: true,
hidden: true,
formatter: (cellContent, resort) => <>{resort.id}</>,
},
{
dataField: "name",
text: "Resort",
sort: true,
formatter: (cellContent, resort) => (
<>
<h5 className="font-size-14 mb-1">
<Link to="#" className="text-dark">
{resort.name}
</Link>
</h5>
<p className="text-muted mb-0">{resort.name}</p>
</>
),
},
{
dataField: "address",
text: "Address",
sort: true,
},
{
dataField: "phone",
text: "Phone",
sort: true,
},
{
dataField: "details",
text: "Details",
sort: true,
},
{
dataField: "city",
text: "City",
sort: true,
formatter: (cellContent, resort) => (
<>
<h5 className="font-size-14 mb-1">
<Link to="#" className="text-dark">
{resort.city}
</Link>
</h5>
<p className="text-muted mb-0">{resort.city}</p>
</>
),
},
{
dataField: "state",
text: "State",
sort: true,
},
{
dataField: "country",
text: "Country",
sort: true,
},
{
dataField: "menu",
isDummyField: true,
editable: false,
text: "Action",
formatter: (cellContent, resort) => (
<div className="d-flex gap-3">
<Link
to={"/resort-detail/" + resort}
>
<i
className="mdi mdi-eye font-size-18"
id="resortdetails"
></i>
</Link>
<Link className="text-success" to="#">
<i
className="mdi mdi-pencil font-size-18"
id="edittooltip"
onClick={() => this.handleResortClick(resort)}
></i>
</Link>
<Link className="text-danger" to="#">
<i
className="mdi mdi-delete font-size-18"
id="deletetooltip"
onClick={() => this.onClickDelete(resort)}
></i>
</Link>
</div>
),
},
],
};
this.handleResortClick = this.handleResortClick.bind(this);
this.toggle = this.toggle.bind(this);
this.handleResortClicks = this.handleResortClicks.bind(this);
this.onClickDelete = this.onClickDelete.bind(this);
}
componentDidMount() {
const { resorts, onGetResorts } = this.props;
const {places, onGetPlaces}=this.props;
if (resorts && !resorts.length) {
onGetResorts();
}
this.setState({ resorts });
if (places && !places.length) {
onGetPlaces();
}
this.setState({ places });
}
toggle() {
this.setState(prevState => ({
modal: !prevState.modal,
}));
}
handleResortClicks = () => {
this.setState({ resort: "", isEdit: false });
this.toggle();
};
componentDidUpdate(prevProps, prevState, snapshot) {
let data = yield select(getResortsSuccess);
console.log(data);
const { resorts } = this.props;
if (!isEmpty(resorts) && size(prevProps.resorts) !== size(resorts)) {
this.setState({ resorts: {}, isEdit: false });
}
}
onPaginationPageChange = page => {
if (
this.node &&
this.node.current &&
this.node.current.props &&
this.node.current.props.pagination &&
this.node.current.props.pagination.options
) {
this.node.current.props.pagination.options.onPageChange(page);
}
};
/* Insert,Update Delete data */
toggleDeleteModal = () => {
this.setState(prevState => ({
deleteModal: !prevState.deleteModal,
}));
};
onClickDelete = (resorts) => {
this.setState({ resorts: resorts });
this.setState({ deleteModal: true });
};
handleDeleteResort = () => {
const { onDeleteResort } = this.props;
const { resorts } = this.state;
if (resorts.id !== undefined) {
onDeleteResort(resorts);
this.setState({ deleteModal: false });
}
};
handleResortClick = arg => {
const resort = arg;
this.setState({
resort: {
id: resort.id,
name: resort.name,
address: resort.address,
phone:resort.phone,
details: resort.details,
placeId:resort.placeId,
city:resort.city,
state: resort.state,
country: resort.country,
},
isEdit: true,
});
this.toggle();
};
render() {
const { SearchBar } = Search;
const { resorts } = this.props;
const { places } = this.props;
const { isEdit,deleteModal} = this.state;
const { onAddNewResort, onUpdateResort } = this.props;
const { selectedResort } = this.state;
const resort= this.state.resort;
// console.log(resort);
const pageOptions = {
sizePerPage: 10,
totalSize: resorts.length, // replace later with size(users),
custom: true,
};
const selectRow = {
mode: "checkbox",
};
return (
<React.Fragment>
<DeleteModal
show={deleteModal}
onDeleteClick={this.handleDeleteResort}
onCloseClick={() => this.setState({ deleteModal: false })}
/>
<div className="page-content">
<MetaTags>
<title>Resort List | Wedding Destination Admin & Dashboard</title>
</MetaTags>
<Container fluid>
{/* Render Breadcrumbs */}
<Breadcrumbs title="Resorts" breadcrumbItem="Resorts List" />
<Row>
<Col lg="12">
<Card>
<CardBody>
<PaginationProvider
pagination={paginationFactory(pageOptions)}
keyField="id"
columns={this.state.resortsListColumns}
data={resorts}
>
{({ paginationProps, paginationTableProps }) => (
<ToolkitProvider
keyField="id"
columns={this.state.resortsListColumns}
data={resorts}
search
>
{toolkitprops => (
<React.Fragment>
<Row className="mb-2">
<Col sm="4">
<div className="search-box ms-2 mb-2 d-inline-block">
<div className="position-relative">
<SearchBar
{...toolkitprops.searchProps}
/>
<i className="bx bx-search-alt search-icon" />
</div>
</div>
</Col>
<Col sm="8">
<div className="text-sm-end">
<Button
color="primary"
className="font-16 btn-block btn btn-primary"
onClick={this.handleResortClicks}
>
<i className="mdi mdi-plus-circle-outline me-1" />
Create New Resort
</Button>
</div>
</Col>
</Row>
<Row>
<Col xl="12">
<div className="table-responsive">
<BootstrapTable
{...toolkitprops.baseProps}
{...paginationTableProps}
selectRow={selectRow}
defaultSorted={defaultSorted}
classes={
"table align-middle table-nowrap table-hover"
}
bordered={false}
striped={false}
responsive
ref={this.node}
/>
<Modal
isOpen={this.state.modal}
className={this.props.className}
>
<ModalHeader
toggle={this.toggle}
tag="h4"
>
{!!isEdit ? "Edit Resort" : "Add Resort"}
</ModalHeader>
<ModalBody>
<Formik
enableReinitialize={true}
initialValues={{
name:
(resort && resort.name) || "",
address:
(resort && resort.address) ||
"",
phone:
(resort && resort.phone) ||
"",
details:
(resort && resort.details) ||
"",
placeId: (resort && resort.placeId) ||
"",
city:
(resort && resort.city) || "",
state:
(resort && resort.state) || "",
country:
(resort && resort.country) ||
"",
}}
validationSchema={Yup.object().shape({
name: Yup.string().required(
"Please Enter Resort Name"
),
phone: Yup.number().required(
"Please Enter Phone"
),
address: Yup.string().required(
"Please Enter Address"
),
details: Yup.string().required(
"Please Enter Details"
),
city:
Yup.string().required(
"Please Enter City"
),
state:
Yup.string().required(
"Please Enter State"
),
country:
Yup.string().required(
"Please Enter Country"
),
})}
onSubmit={values => {
console.log(">>>values>>>>> "+JSON.stringify(values));
if (isEdit) {
const updateResort = {
id: resort.id,
name: values.name,
address: values.address,
phone: values.phone,
details: values.details,
// placeId:updatePlaceId.placeId,
placeId:this.state.placeId,
city: values.city,
state: values.state,
country: values.country,
};
// update user
onUpdateResort(updateResort);
} else {
// const newPlaceId=places.filter(x=>x.city===values["city"]);
const newResort = {
// id: values["id"],
name: values["name"],
address: values["address"],
phone: values["phone"],
details: values["details"],
placeId:this.state.placeId,
// placeId:newPlaceId.placeId
city: values["city"],
state: values["state"],
country: values["country"],
};
// save new resort
onAddNewResort(newResort);
}
this.setState({ selectedResort: null });
this.toggle();
}}
>
{({ errors, status, touched,setFieldValue,handleChange}) => (
<Form>
<Row>
<Col className="col-12">
<div className="mb-3">
<Label className="form-label">
Resort Name
</Label>
<Field
name="name"
type="text"
className={
"form-control" +
(errors.name &&
touched.name
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="name"
component="div"
className="invalid-feedback"
/>
</div>
<div className="mb-3">
<Label className="form-label">
Address
</Label>
<Field
name="address"
type="text"
className={
"form-control" +
(errors.address &&
touched.address
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="address"
component="div"
className="invalid-feedback"
/>
</div>
<div className="mb-3">
<Label className="form-label">
Phone
</Label>
<Field
name="phone"
type="tel"
className={
"form-control" +
(errors.phone &&
touched.phone
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="phone"
component="div"
className="invalid-feedback"
/>
</div>
<div className="mb-3">
<Label className="form-label">
Details
</Label>
<Field
name="details"
type="text"
className={
"form-control" +
(errors.details &&
touched.details
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="details"
component="div"
className="invalid-feedback"
/>
</div>
<div className="mb-3">
<Label className="form-label">
City
</Label>
<Field
onChange={(e) => {
handleChange(e);
let selectedGeo=places.filter((x) => x.city === e.target.value)
if(selectedGeo.length>0)
{
this.setState({placeId:selectedGeo[0].id});
setFieldValue("state", selectedGeo[0].state)
setFieldValue("country", selectedGeo[0].country)
}
}}
type="text"
name="city"
list="datalistCity"
className={
"form-control" +
(errors.city &&
touched.city
? " is-invalid"
: "")
}
multiple={false}
/>
<datalist id="datalistCity">
{
places.map((place) => {
return (
<option
value={place.city}
key={place.id}
>
</option>
);
})}
</datalist>
<ErrorMessage
name="city"
component="div"
className="invalid-feedback"
/>
</div>
<div className="mb-3">
<Label className="form-label">
State
</Label>
<Field
name="state" disabled
type="text"
className="form-control"
/>
<ErrorMessage
name="state"
component="div"
className="invalid-feedback"
/>
</div>
<div className="mb-3">
<Label className="form-label">
Country
</Label>
<Field
type="text"
name="country"
disabled
className="form-control"
/>
<ErrorMessage
name="country"
component="div"
className="invalid-feedback"
/>
</div>
</Col>
</Row>
<Row>
<Col>
<div className="text-end">
<button
type="submit"
className="btn btn-success save-resort"
>
Save
</button>
</div>
</Col>
</Row>
</Form>
)}
</Formik>
</ModalBody>
</Modal>
</div>
</Col>
</Row>
<Row className="align-items-md-center mt-30">
<Col className="pagination pagination-rounded justify-content-end mb-2">
<PaginationListStandalone
{...paginationProps}
/>
</Col>
</Row>
</React.Fragment>
)}
</ToolkitProvider>
)}
</PaginationProvider>
</CardBody>
</Card>
</Col>
</Row>
</Container>
</div>
</React.Fragment>
);
}
}
ResortsList.propTypes = {
resorts: PropTypes.array,
places:PropTypes.array,
className: PropTypes.any,
onGetResorts: PropTypes.func,
onGetPlaces: PropTypes.func,
onAddNewResort: PropTypes.func,
onDeleteResort: PropTypes.func,
onUpdateResort: PropTypes.func,
};
const mapStateToProps = ({ resorts,places }) => ({
resorts: resorts.resorts,
places:places.places
});
const mapDispatchToProps = dispatch => ({
onGetResorts: () => dispatch(getResorts()),
onGetPlaces: () => store.dispatch(getPlaces()),
onAddNewResort: resort => dispatch(addNewResort(resort)),
onUpdateResort: resort => dispatch(updateResort(resort)),
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(withRouter(ResortsList));
resort(in redux store) - saga.js
import { call, put, takeEvery } from "redux-saga/effects" // Crypto Redux States import { GET_RESORTS, GET_RESORT_DETAILS, ADD_NEW_RESORT, DELETE_RESORT, UPDATE_RESORT } from "./actionTypes" import { addResortFail, addResortSuccess, updateResortSuccess, updateResortFail, deleteResortSuccess, deleteResortFail, } from "./actions" //Include Both Helper File with needed methods import { addNewResort, updateResort, deleteResort } from "../../helpers/fakebackend_helper" const getUserName = () => { if (localStorage.getItem("authUser")) { const obj = JSON.parse(localStorage.getItem("authUser")) return obj; } } function* fetchResorts() { try { let obj = getUserName(); const response = yield call(getResorts, obj.accessToken) console.log("resort list ---- "+JSON.stringify(response)) yield put(getResortsSuccess(response)) } catch (error) { yield put(getResortsFail(error)) } } function* onAddNewResort({ payload: resort }) { try { let obj = getUserName(); const response = yield call(addNewResort, resort, obj.accessToken) console.log("------- "+JSON.stringify(response)) yield put(addResortSuccess(response)) } catch (error) { yield put(addResortFail(error)) } } function* onUpdateResort({ payload: resort }) { try { //const obj = JSON.parse(localStorage.getItem("authUser")); console.log("Saga..... edit >>>. "+JSON.stringify(resort)) let obj = getUserName(); const response = yield call(updateResort, resort, obj.accessToken) yield put(updateResortSuccess(response)) } catch (error) { yield put(updateResortFail(error)) } } function* resortsSaga() { yield takeEvery(GET_RESORTS, fetchResorts) yield takeEvery(GET_RESORT_DETAILS, fetchResortDetails) yield takeEvery(ADD_NEW_RESORT, onAddNewResort) yield takeEvery(UPDATE_RESORT, onUpdateResort) yield takeEvery(DELETE_RESORT, onDeleteResort) } export default resortsSagaresort(in redux store) - action.js
import { ADD_NEW_RESORT, ADD_RESORT_SUCCESS, ADD_RESORT_FAIL, UPDATE_RESORT, UPDATE_RESORT_SUCCESS, UPDATE_RESORT_FAIL, } from "./actionTypes" export const addNewResort = resort => ({ type: ADD_NEW_RESORT, payload: resort, }) export const addResortSuccess = resort => ({ type: ADD_RESORT_SUCCESS, payload: resort, }) export const addResortFail = error => ({ type: ADD_RESORT_FAIL, payload: error, }) export const updateResort = resort => ({ type: UPDATE_RESORT, payload: resort, }) export const updateResortSuccess = resort => ({ type: UPDATE_RESORT_SUCCESS, payload: resort, }) export const updateResortFail = error => ({ type: UPDATE_RESORT_FAIL, payload: error, })
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
