Reputation: 1970
I am using react hooks and I have a child component Addmember
and a parent component MemberList
. Within the member list the user clicks add member and a model pops up (the child component) and then user can input some form data then submit the form. The form then adds the new member to the MongoDB collection. .then()
it called parentCallback(true)
to flip the parent state of loading
to true
which will then trigger a component re-render. Which is successfully working.
The problem here which I cant get my head around the issue is that upon the parent re-render when it hits the database to getMembers
its not returning the newly added member. If I refresh the page the same call will successfully grab all the members including the recent addition.
MemberList.js:
import React, { useState, useEffect } from "react";
import Spinner from "react-bootstrap/Spinner";
import { toast } from "react-toastify";
import { css } from "glamor";
import "react-toastify/dist/ReactToastify.css";
import "react-datepicker/dist/react-datepicker.css";
import "../Styles/ReactDatePicker.css";
import Member from "./Member";
import AddMemberModal from "../models/AddMember";
import RemoveMemberModel from "../models/RemoveMember";
const { getFamily, getMembers } = require("../Utils/Service");
const MemberList = () => {
const [family, setFamily] = useState({});
const [familyMembersList, setFamilyMembersList] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
if (loading) {
fetchData();
}
}, [loading]);
async function fetchData() {
getFamily.then(result => {
console.log('get family');
setFamily(result);
});
getMembers.then(result => {
try {
console.log('set family list');
console.log(result);
setFamilyMembersList(
result.map((child, index) => (
<Member
key={index}
index={child._id}
balance={child.balance}
firstName={child.firstName}
lastName={child.lastName}
birthday={child.birthday}
role={child.role[0]}
/>
))
);
} catch (e) {
toast.error("500: Error with Service Call", {
position: "top-right",
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
className: css({
background: "#ed5565 !important"
})
});
} finally {
setLoading(false);
}
});
}
function handleCallback() {
setLoading(true);
}
return (
<div className="ibox">
<div className="ibox-title">
<h5>{family.name}</h5>
<div className="ibox-tools">
<span className="label label-warning-light float-right">
{familyMembersList.length} Member(s)
</span>
</div>
</div>
<div className="ibox-content">
<div className="feed-activity-list">
{loading ? (
<Spinner animation="grow" role="status" variant="dark">
<span className="sr-only">Loading...</span>
</Spinner>
) : (
familyMembersList
)}
</div>
<div className="d-flex">
<a
className="btn btn-primary text-white m-t"
data-toggle="modal"
data-target={"#newMemberModel"}
>
<i className="fa fa-plus"></i> New Member
</a>
<a
className="btn btn-danger text-white m-t m-l"
data-toggle="modal"
data-target={"#removeMemberModel"}
>
<i className="fa fa-minus"></i> Remove Member
</a>
</div>
</div>
{/* Add Member Model */}
<AddMemberModal parentCallback={handleCallback}/>
{/* Remove Member Model */}
<RemoveMemberModel />
</div>
);
};
export default MemberList;
AddMember.js
import React, { useState, useEffect } from "react";
import { toast } from "react-toastify";
import DatePicker from "react-datepicker";
import useForm from "react-hook-form";
import { css } from "glamor";
import "react-toastify/dist/ReactToastify.css";
import "react-datepicker/dist/react-datepicker.css";
import "../Styles/ReactDatePicker.css";
const { postMember } = require("../Utils/Service");
const AddMemberModal = ({parentCallback}) => {
const [date, setDate] = useState(new Date());
const { handleSubmit, register, errors } = useForm();
// const handleDateChange = date => {
// setDate(date)
// }
const onSubmit = (data) => {
try {
data.familyId = "5dddf14df965552b3da57be1";
postMember(data).then(async () => parentCallback(true));
} catch (error) {
toast.error("500: Error with Service Call", {
position: "top-right",
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
className: css({
background: "#ed5565 !important"
})
});
}
};
return (
<div
className="modal inmodal"
id={"newMemberModel"}
tabIndex="-1"
role="dialog"
style={{ display: "none" }}
aria-hidden="true"
>
<div className="modal-dialog">
<div className="modal-content animated fadeIn">
<div className="modal-header">
<button type="button" className="close" data-dismiss="modal">
<span aria-hidden="true">×</span>
<span className="sr-only">Close</span>
</button>
<h4 className="modal-title">New Family Member</h4>
</div>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="modal-body">
<div className="row">
<div className="col">
<div className="form-group">
<label>First Name</label>
<input
type="text"
placeholder="First Name"
className="form-control"
name="firstName"
ref={register({
required: true,
pattern: {
value: /^[a-zA-Z]+$/i,
message: "Invalid First Name"
}
})}
/>
<div className="text-danger">
{errors.firstName && errors.firstName.message}
</div>
</div>
<div className="form-group">
<label className="font-normal">Birthday</label>
<div className="input-group date">
{/* <DatePicker
selected={date}
onChange={handleDateChange}
placeholderText="Click to select a date"
isClearable
peekNextMonth
showMonthDropdown
showYearDropdown
dropdownMode="select"
ref={e =>register({
name: "Birthday",
required: false
})}
/> */}
<input
type="text"
placeholder="01/01/01"
className="form-control"
name="birthDate"
ref={register({
required: false
})}
/>
</div>
</div>
</div>
<div className="col">
<div className="form-group">
<label>Last Name</label>
<input
type="text"
placeholder="Last Name"
className="form-control"
name="lastName"
ref={register({
required: true,
pattern: {
value: /^[a-zA-Z]+$/i,
message: "Invalid Last Name"
}
})}
/>
<div className="text-danger">
{errors.lastName && errors.lastName.message}
</div>
</div>
<div className="form-group">
<label>Role</label>
<select
className="custom-select"
name="role"
ref={register({ required: true })}
>
<option defaultValue>Select Role</option>
<option value="Adult">Adult</option>
<option value="Child">Child</option>
</select>
</div>
</div>
</div>
</div>
<div className="modal-footer">
<button
type="button"
className="btn btn-white"
data-dismiss="modal"
>
Close
</button>
<button type="submit" className="btn btn-primary">
Add Member
</button>
</div>
</form>
</div>
</div>
</div>
);
};
export default AddMemberModal;
The order in which I can see in the console of the console happening is:
[Log] get family
[Log] set family list
[Log] [Object, Object, Object, Object, Object, Object, Object, Object, Object, Object] (10)
The object its return is missing the most recent member addition. I have tried adding timeouts thinking it may be a timing issue but this had no change in this issue.
Upvotes: 1
Views: 449
Reputation: 18592
you can add the submitted data to the array instead of refreshing the page
in MemberList.js
file, change your handleCallback
to this :
function handleCallback(data){
const arr = [...familyMembersList];
let newMember = (
<Member
key={data._id}
index={data._id}
balance={0}
firstName={data.firstName}
lastName={data.lastName}
birthday={data.birthday}
role={data.role}
/>
)
arr.push(newMember);
console.log(arr);
setFamilyMembersList(arr);
setLoading(false);
}
and in your AddMember.js
change onSubmit
method to this :
const onSubmit = (data) => {
try {
data.familyId = "5dddf14df965552b3da57be1";
postMember(data).then(async () => parentCallback(data));
} catch (error) {
...
}
};
Upvotes: 1