how get new data from database in reactjs - javascript

My projct is a note app where you can can add notes and delete notes. I want to get data from the database and show it but I have to delete note twice, add note or reload page to show the new data.
notesList.jsx
this is my main component
i send getNotes() to another component for i can get new datas
const NotesList = () => {
const [notes, setNotes] = useState([]);
const getNotes = async () => {
const getNoteInformation = {
email: localStorage.getItem("tokenEmail"),
};
const response = await axios.post(
"http://localhost:9000/api/note/get",
getNoteInformation
);
try {
setNotes(response.data.data);
} catch (error) {}
};
const handleAddNote = async (tasktext) => {
const addNoteInformation = {
email: localStorage.getItem("tokenEmail"),
taskText: tasktext,
date: moment(new Date()).locale("fa").format("YYYY/MM/DD").toString(),
};
if (addNoteInformation.email && addNoteInformation.taskText) {
try {
await axios.post(
"http://localhost:9000/api/note/add",
addNoteInformation
);
} catch (error) {}
}
};
const handleDeleteNote = async (id) => {
const deletedInformaion = {
email: localStorage.getItem("tokenEmail"),
noteId: id,
};
if (deletedInformaion.email && deletedInformaion.noteId) {
await axios.post(
"http://localhost:9000/api/note/deleted",
deletedInformaion
);
}
};
useEffect(() => {
getNotes();
}, []);
return (
<>
<Navbar />
<div className="container">
<div className="row">
{notes
.filter((notes) => notes != null)
.map((notes, index) => (
<Note
key={index}
text={notes.text}
date={notes.date}
id={notes._id}
deletenote={handleDeleteNote}
getnote={getNotes}
/>
))}
<AddNote getnote={getNotes} addnote={handleAddNote} />
</div>
</div>
</>
);
};
note.jsx
const Note = (props) => {
const handleDeleteNote = () => {
props.deletenote(props.id);
props.getnote();
};
return (
<>
<div className="col-lg-4 col-md-6 col-sm-12 p-4">
<div className="note d-flex flex-column">
<span className="note-top overflow-auto m-2 ps-2">{props.text}</span>
<div className="note-bottom d-flex justify-content-between flex-row-reverse mt-auto">
<small>{props.date}</small>
<MdDelete
onClick={handleDeleteNote}
className="delete-icon"
size="1.3rem"
color="#bb86fc"
/>
</div>
</div>
</div>
</>
);
};
addNote.jsx
const AddNote = (props) => {
let addNoteText = useRef();
const handleAddNote = async () => {
props.addnote(addNoteText.current.value);
props.getnote();
addNoteText.current.value = "";
};
return (
<div className="col-lg-4 col-md-6 col-sm-12 p-4">
<div className="add-note-box d-flex flex-column justify-content-between">
<div className="top-box">
<textarea
placeholder="یادداشت خود را وارد کنید ......"
class="form-control"
rows={7}
ref={addNoteText}
></textarea>
</div>
<BsFillPlusCircleFill
onClick={handleAddNote}
className="plus-icon"
size="1.3rem"
color="#bb86fc"
/>
</div>
</div>
);
};

You didn't update state after sending the respective add and delete request, that's why you need to refresh to get the updated data. This is the fix:
const handleAddNote = async (tasktext) => {
const addNoteInformation = {
email: localStorage.getItem("tokenEmail"),
taskText: tasktext,
date: moment(new Date()).locale("fa").format("YYYY/MM/DD").toString(),
};
if (addNoteInformation.email && addNoteInformation.taskText) {
try {
await axios.post(
"http://localhost:9000/api/note/add",
addNoteInformation
);
setNotes([...notes, addNoteInformation]); // update state using spread operator and put the new one to the end
} catch (error) {}
}
};
const handleDeleteNote = async (id) => {
const deletedInformaion = {
email: localStorage.getItem("tokenEmail"),
noteId: id,
};
if (deletedInformaion.email && deletedInformaion.noteId) {
await axios.post(
"http://localhost:9000/api/note/deleted",
deletedInformaion
);
setNotes(notes.filter(note => note.id !== id)) // update state using filter function
}
};
Alternativelly, when adding notes, you can update state using the response object from your request, since the id of newly created note maybe generated by your database. It dependes on the actual response object from the axios request:
const response = await axios.post();
setNotes([...notes, response.data]);

Related

How to create dynamic input field for searching in React JS and Node JS

I want to create multiple dynamic fields for searching (so I can add or subtract text fields to write the search), here's my code on the backend to find the requestor and the frontend which is still not integrated with the backend (I still following the tutorial steps to create a form whose fields are dynamic)
how do I get the two parts to be properly integrated and can produce data search results when pressing the handlesubmit button?
here I use react JS, node JS, express, and MySQL
thanks
for backend
export const getRequestor = async (req: TypedRequestQuery<{lastId: string, search_requestor:string}>, res: Response) =>{
const searchRequestor = req.query.search_requestor || "";
const resultRequestor = await Product.findAll({
where:{
[Op.or]: [
{title_dev:{ //requestor
[Op.like]: '%'+searchRequestor+'%'
}}]
},
order:[
['id_project', 'ASC']
]
});
res.json({
resultRequestor: resultRequestor,
});
}
for frontend
const Audit = () => {
const [requestors, setRequestors] = useState([]);
const [keyword, setKeyword] = useState("");
const [query, setQuery] = useState("");
useEffect(() => {
getRequestor();
}, [keyword]);
const getRequestor = async () => {
const response = await axios.get(
`http://localhost:5001/requestor?search_requestor=${keyword}`
);
setRequestors(response.data.resultRequestor);
};
const [inputFieldsRequestor, setInputFieldsRequestor] = useState([
{idRequestor: uuidv4(), requestor: ''},
]);
const handleSubmitRequestor = (e) => {
e.preventDefault();
console.log("InputFieldsRequestor", inputFieldsRequestor);
};
const handleSubmitAll = (e) => {
e.preventDefault();
console.log("InputFieldsRequestor", inputFieldsRequestor);
console.log("InputFieldsPeriod", inputFieldsPeriod);
};
const handleChangeInputRequestor = (idRequestor, event) => {
const newInputFieldsRequestor = inputFieldsRequestor.map(i => {
if(idRequestor === i.idRequestor){
i[event.target.name] = event.target.value
}
return i;
})
setInputFieldsRequestor(newInputFieldsRequestor);
}
const handleAddFieldsRequestor = () =>{
setInputFieldsRequestor([...inputFieldsRequestor, {idRequestor: uuidv4(), requestor:''}])
// setRequestors([...requestors]);
}
const handleRemoveFieldsRequestor = idRequestor => {
const values = [...inputFieldsRequestor];
values.splice(values.findIndex(value => value.idRequestor === idRequestor), 1);
setInputFieldsRequestor(values);
}
const submitrequestor= (e) =>{
e.preventDefault();
setKeyword(query);
console.log("Requestor", requestors);
}
return(
<div>
<form className='form-horizontal' onSubmit={handleSubmitRequestor}>
{inputFieldsRequestor.map(inputFieldRequestor => (
<div key={inputFieldRequestor.idRequestor}>
<div className="form-group row">
<label className="col-sm-2 col-form-label">Requestor</label>
<div className="col-sm-10">
<input type="text"
name="requestor"
className="form-control"
variant="filled"
value={inputFieldRequestor.requestor}
onChange={event => handleChangeInputRequestor(inputFieldRequestor.idRequestor, event)}
placeholder="Requestor" />
<button className="offset-sm-1 col-sm-2" disabled={inputFieldsRequestor.length === 1}
onClick={() => handleRemoveFieldsRequestor(inputFieldRequestor.idRequestor)}>
-
</button >
<button className="offset-sm-1 col-sm-2" onClick={handleAddFieldsRequestor}>
+
</button>
</div>
</div>
</div>
))}
<button
className="btn btn-danger"
type='submit'
onClick={handleSubmitRequestor}
>
send
</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>)}

List Items on react component not updating using useReducer() when clicking delete button

I am using use reducer inside a ReactContext to handle some signatures display. On Messages and messagesForm components the dispatch function works fine. But inside prevMessages it doesn't.
I tried console log (json) but it is also not working inside the handleDeleteMes() function.
I have tried everything I could think of for solving this problem.
Here I provide my code:
This is the parent component -Messages.jsx:
function Messages() {
const {dispatch, signing, signatures} = useSignatureContext();
const { user } = useUserContext()
const thisPalRef = useRef([]);
useEffect(()=>{
const fetchSignatures = async()=>{
const response = await fetch('/api/signatures/sent', {
method: 'GET',
headers: {
'Authorization': `Bearer ${user.token}`
}
})
const json = await response.json();
if (response.ok) {
json.forEach((mes)=>{
mes.recipient_id === signing._id && thisPalRef.current.push(mes)
})
}
dispatch({type:'SET_SIGNATURES', payload: thisPalRef.current}) //It works fine
}
fetchSignatures()
},[signing._id, user.token, dispatch])
return (
<motion.div
initial= {{opacity: 0, height:'10%',width:'85%', x:0}}
animate= {{opacity: 1, height:'100%', width:'95%', x:0}}
exit= {{opacity:0, height:'10%',width:'85%', x:0}}
transition={{ duration: 0.2 }}>
<div className="d-flex flex-column align-items-start p-1 pt-4">
<MessageForm />
<div className='prevMsg_container d-flex flex-column align-items-end p-3 mb-5'>
<header className='my-5 align-self-center'>
<h2>Previous Messages</h2>
<hr />
</header>
{
signatures ? signatures.map(mes => {
console.log(signatures)
return <PrevMessages key={mes._id} mes={mes}/>
}) : <small className="text-muted">No messages sent</small>
}
</div>
</div>
</motion.div>
)
}
one child component -PrevMessages.jsx
function PrevMessages({mes}) {
const {dispatch} = useSignatureContext();
const {user} = useUserContext();
const handleDeleteMes = async() => {
if (!user) {
return
}
const response = await fetch('/api/signatures/' + mes._id, {
method:'DELETE',
headers: {
'Authorization': `Bearer ${user.token}`
}
})
const json = await response.json();
console.log(json) //it doesn't work
if (response.ok) {
dispatch({type: 'DELETE_SIGNATURE', payload: json}) //it doesnt work
}
}
return <figure className="text-end">
<blockquote className="blockquote">
<p className='prevMsg_text mt-3 text-end'>{mes.message}</p>
</blockquote>
<figcaption className="blockquote-footer text-end">
<cite title="Sent date">{formatRelative (new Date(mes.updatedAt), new Date())}</cite>
</figcaption>
<IconContext.Provider value={{ color: '#ea1537', className: "delete_icon" }}>
<span className="text-end delete_msg_btn" onClick={() => handleDeleteMes(mes._id)}><RiChatDeleteFill/></span>
</IconContext.Provider>
</figure>
}
and another child component -MessageForm.jsx
function MessageForm() {
const {signing, dispatch} = useSignatureContext();
const { user } = useUserContext()
const [myMessage, setMyMessage] = useState('');
const [error, setError] = useState(null);
const [emptyFields, setEmptyFields] = useState([]);
const handleSubmitMessage = async (e) => {
e.preventDefault()
if (!user) {
setError('You must be logged in')
return
}
const mssg = {
message: myMessage,
recipient: `${signing.name} ${signing.last_name}`,
recipient_id: signing._id,
sender: `${user.name} ${user.last_name}`,
sender_id: user._id,
sender_signature: user.signature
}
const response = await fetch('/api/signatures', {
method: 'POST',
body: JSON.stringify(mssg),
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${user.token}`
}
})
const json = await response.json()
if(!response.ok){
setError(json.error)
setEmptyFields(json.emptyFields)
}
if (response.ok) {
setMyMessage('');
setError(null);
setEmptyFields([]);
console.log('new message sent!', json);
dispatch({type: 'CREATE_SIGNATURE', payload: json}); //works fine
}
}
return (
<>
<h1 className='align-self-center bk_owner_title'>{signing.name}<small className="text-muted">'s Book</small></h1>
<div className="input-group">
<textarea value={myMessage} className={"form-control txtArea pt-4 ps-2 msg_txt_area " + (emptyFields.includes('message') ? ' message_error' : '')} autoFocus onChange={(e)=>setMyMessage(e.target.value)} placeholder={'Dear ' + signing.name + ' ' + signing.last_name + '...'}></textarea>
</div>
<figcaption className='message_footer mx-3 mt-1'>From: {user.signature}</figcaption>
<button type='submit' className='btn btn-success align-self-end' onClick={(e)=>handleSubmitMessage(e)}>Send</button>
{error && <Alert variant='danger' className='mt-3 align-self-center alert_message'>{error}</Alert>}
</>
)
}
And this is the context -SignatureContext.jsx
const SignatureContext = createContext();
const signaturesReducer = (mesState, action) => {
switch (action.type) {
case 'SET_SIGNATURES':
return {
signatures: action.payload
}
case 'CREATE_SIGNATURE':
return {
signatures: [action.payload, ...mesState.signatures]
}
case 'DELETE_SIGNATURE':
return {
signatures: mesState.signatures.filter((s) => s._id !== action.payload._id)
}
default:
return mesState
}
}
const SignatureContextProvider = ({children})=> {
const [signing, setSigning] = useState({});
const [mesState, dispatch] = useReducer(signaturesReducer, {
signatures: null
});
return (
<SignatureContext.Provider value={{signing, setSigning, ...mesState, dispatch}}>
{children}
</SignatureContext.Provider>
)
}
export {SignatureContextProvider, SignatureContext, signaturesReducer};
Thank you in advance, any ideas will be appreciated.
Found the problem in the backend. The controller function wasn't returning JSON response so the frontend function had nothing to work with. For that reason it was stopping after the fetch, I'm using Express and Mongoose.
This was my controller function before:
const deleteSignature = async (req, res)=>{
const {id} = req.params
if(!mongoose.Types.ObjectId.isValid(id)) {
return res.status(404).json({error: 'No such signature'})
}
const signature = await Signature.findOneAndDelete({_id: id})
if(!signature) {
return res.status(404).json({error: 'No such signature'})
}
//nothing returning on success
}
and this is how I fixed it
const deleteSignature = async (req, res)=>{
const {id} = req.params
if(!mongoose.Types.ObjectId.isValid(id)) {
return res.status(404).json({error: 'No such signature'})
}
const signature = await Signature.findOneAndDelete({_id: id})
if(!signature) {
return res.status(404).json({error: 'No such signature'})
}
res.status(200).json(signature) //returning json on success
}
It works perfectly.

How to show changes before submit in react

I have a project that make profile cards for users in react. When user create his card I'm tiring to show him how the card looks before he submit and create it.
At this moment the create card work good but I don't know how to show him the card at the same time.
My state update only when he is submit the form.
create component:
function CreatCard() {
const navigate = useNavigate();
const getUserData = JSON.parse(localStorage.getItem("data")).data;
console.log(getUserData.id);
const [bisCard, setBisCard] = useState({
businessName: "",
businessDescription: "",
businessAddress: "",
businessPhone: "",
businessImage: "",
userId: getUserData.id,
});
const handleCreateCard = async (e) => {
e.preventDefault();
const card = { bisCard };
console.log(card.bisCard);
const requestMethods = {
method: "POST",
headers: {
"Content-Type": "application/json",
token: getUserData.token,
},
body: JSON.stringify(card.bisCard),
};
console.log(getUserData.token);
try {
const response = await fetch(
"http://localhost:8000/cards/create",
requestMethods
);
if (response.ok) navigate("/cusomerPage");
else {
console.log(response.json());
}
} catch (err) {
console.log("!!!!", err);
}
};
const handleChangeName = (e) =>
setBisCard({ ...bisCard, businessName: e.target.value });
const handleChangeDescription = (e) =>
setBisCard({ ...bisCard, businessDescription: e.target.value });
const handleChangeAdress = (e) =>
setBisCard({
...bisCard,
businessAddress: e.target.value,
});
const handleChangephone = (e) =>
setBisCard({ ...bisCard, businessPhone: e.target.value });
const handleChangeImg = (e) =>
setBisCard({ ...bisCard, businessImage: e.target.value });
return (
<div>
<div className="creat-form-container">
<CardForm
handleCreateCard={handleCreateCard}
handleChangeName={handleChangeName}
handleChangeDescription={handleChangeDescription}
handleChangeAdress={handleChangeAdress}
handleChangephone={handleChangephone}
handleChangeImg={handleChangeImg}
/>
<div card-container>
<Card card={bisCard}></Card>
</div>
</div>
</div>
);
}
card component:
import DeleteCard from "../deleteCard/DeleteCard";
import "./Card.scss";
const Card = ({ card }) => {
// console.log(card);
return (
<div className="card col my-2 mx-2 ">
<DeleteCard card={card} />
<img
src={card.businessImage}
className="img-size rounded card-img-top "
alt="BusinessImage"
/>
<div className="card-body">
<h5 className="card-title">{card.businessName}</h5>
<p className="card-text">
<b> {card.businessPhone}</b>
</p>
<p className="card-text">{card.businessDescription}</p>
<p className="card-text">
<small className="text-muted">{card.businessAddress}</small>
</p>
</div>
</div>
);
};
export default Card;
thanks for help

How to add a button and a dropdown in AgGrid Cell in react Functional Component

I have list of data. I am using AgGrid in react to display this data list. For each row i need to display a column having a delete button and a second column having a dropdown and a reset button.
When i click the delete button i need the corresponding row data and when i click reset button i need the dropdown option select as well as the corresponding row data.
I have searched but i am not able to figure out how to do it in react functional components. I have found that i need to use ICellRendererReactComp but i am not sure how as i am new to react and AgGrid
My current code looks something like this :
import React, { useState } from "react";
import toaster from "toasted-notes";
import { apiRequest, errorHandler } from "../../utilis/apiRequest";
import { columnDefsFromArr } from "../Threads/columnDefs";
import { AgGridReact, ICellRendererReactComp } from "ag-grid-react";
import { isResourcePresent } from "../../utilis/helper";
function Sessions(props) {
const [email, setEmail] = useState("");
const [reid, setReid] = useState(null);
const [sessionsResp, setSessionsResp] = useState(null);
const [columnDefs, setColumnDefs] = useState(null);
const [rowData, setRowData] = useState(null);
const defaultColDef = {
sortable: true,
filter: true,
resizable: true,
};
const colsToExlude = ["requestId"];
const resetSessionOptions = [
"None",
"ClearClientCache",
"PasswordChange",
"Suspended",
"InvalidSession",
"Expired",
];
const handleEmailChange = (e) => {
setEmail(e.target.value);
};
const handleGetSession = () => {
setReid(email);
getSessions({ reid: email, env: props.env });
};
const getSessions = (data) => {
console.log(data, reid);
let isError = validateForm(data);
if (isError.status) {
apiRequest(
props.sessionToken,
"get_session",
data,
(res) => {
if (res.status === 200) {
console.log(res.data);
setRowData(res.data.sessions);
makeGrid(res.data);
}
},
(err) => {
errorHandler(err);
}
);
} else {
toaster.notify(isError.msg, {
duration: 1500,
});
}
};
const handleDelete = (data) => {
console.log(data);
};
const handleReset = (data) => {
console.log(data);
};
const makeGrid = (data) => {
let cols = [];
data.sessions.map((ele) => {
Object.keys(ele).map((key) => cols.push(key));
});
let localCols = [];
if (isResourcePresent(props.resources, "del_session")) {
localCols.push({
headerName: "Delete Sessio",
});
}
if (isResourcePresent(props.resources, "reset_session")) {
localCols.push({
headerName: "Reset Session"
});
}
cols = [...new Set(cols)];
colsToExlude.map((key) => {
let ind = cols.indexOf(key);
if (ind > -1) {
cols.splice(ind, 1);
}
});
let finalColDefs = [...localCols, ...columnDefsFromArr(cols)];
console.log(finalColDefs);
setColumnDefs(finalColDefs);
};
const validateForm = (data) => {
if (data.reid.trim() === "") {
return { status: false, msg: "Email/Email Id is reqd" };
} else {
return { status: true };
}
};
return (
<div className="container-fluid">
<div>
<h5>Get Sessions Information</h5>
</div>
<div className="card mt-2 p-3 bg-red shadow p-3 mb-5 bg-white rounded">
<div className="row m-2">
<div className="col-sm-6">
<input
type="text"
className="form-control"
value={email || ""}
placeholder="Email / Email ID"
onChange={handleEmailChange}
/>
</div>
<div className="col-sm-2">
<button className="button btn-primary" onClick={handleGetSession}>
Get Information
</button>
</div>
</div>
</div>
{rowData == null ? null : (
<div className="card mt-2 p-3 bg-red shadow p-3 mb-5 bg-white rounded">
<div
className="ag-theme-balham"
style={{ height: "500px", width: "100%" }}
>
<AgGridReact
columnDefs={columnDefs}
rowData={rowData}
></AgGridReact>
</div>
</div>
)}
</div>
);
}
export { Sessions };
handleDelete : this is the function i want to call when that delete button is clicked for some corresponding row,
resetSessionOptions : this is the dropdown list options i need to display in second column along with Reset button.
handleReset : this the function i want to call when that Reset button besides the dropdown is clicked for some corresponding row
So I searched a lot and finally came across this example : https://stackblitz.com/edit/angular-ag-grid-button-renderer?file=src%2Fapp%2Frenderer%2Fbutton-renderer.component.ts
Above example is for Angular and uses classes.
I figured out how to do it in react Functional Components. I did not used any interface or something but implemented all methods in above given example and made Renderer classes for the two columns i needed. You can see the code below.
UPDATE : My this solution works but this is causing my all other state variables to reset to initial state. I am not sure why thats happening. I am looking for a solution for that.
Session.js
import React, { useState } from "react";
import toaster from "toasted-notes";
import { apiRequest, errorHandler } from "../../utilis/apiRequest";
import { columnDefsFromArr } from "../Threads/columnDefs";
import { AgGridReact, ICellRendererReactComp } from "ag-grid-react";
import { isResourcePresent } from "../../utilis/helper";
import { ButtonRenderer } from "./ButtonRenderer";
import { DropDownRender } from "./DropDownRender";
function Sessions(props) {
const [email, setEmail] = useState("");
const [reid, setReid] = useState(null);
const [sessionsResp, setSessionsResp] = useState(null);
const [columnDefs, setColumnDefs] = useState(null);
const [rowData, setRowData] = useState(null);
const frameworkComponents = {
buttonRenderer: ButtonRenderer,
dropDownRenderer: DropDownRender,
};
const defaultColDef = {
sortable: true,
filter: true,
resizable: true,
};
const colsToExlude = ["requestId"];
const resetSessionOptions = [
"None",
"ClearClientCache",
"PasswordChange",
"Suspended",
"InvalidSession",
"Expired",
];
const handleEmailChange = (e) => {
setEmail(e.target.value);
};
const handleGetSession = () => {
setReid(email);
getSessions({ reid: email, env: props.env });
};
const getSessions = (data) => {
console.log(data, reid);
let isError = validateForm(data);
if (isError.status) {
apiRequest(
props.sessionToken,
"get_session",
data,
(res) => {
if (res.status === 200) {
console.log(res.data);
setRowData(res.data.sessions);
makeGrid(res.data);
}
},
(err) => {
errorHandler(err);
}
);
} else {
toaster.notify(isError.msg, {
duration: 1500,
});
}
};
const handleDelete = (data) => {
console.log("DEL", data);
};
const handleReset = (data) => {
console.log("RESET", data);
};
const makeGrid = (data) => {
let cols = [];
data.sessions.map((ele) => {
Object.keys(ele).map((key) => cols.push(key));
});
let localCols = [];
if (isResourcePresent(props.resources, "del_session")) {
localCols.push({
headerName: "Delete Sessio",
cellRenderer: "buttonRenderer",
cellRendererParams: {
onClick: handleDelete,
label: "Delete",
},
});
}
if (isResourcePresent(props.resources, "reset_session")) {
localCols.push({
headerName: "Reset Session",
cellRenderer: "dropDownRenderer",
cellRendererParams: {
onClick: handleReset,
label: "RESET",
dropDown: resetSessionOptions,
},
});
}
cols = [...new Set(cols)];
colsToExlude.map((key) => {
let ind = cols.indexOf(key);
if (ind > -1) {
cols.splice(ind, 1);
}
});
let finalColDefs = [...localCols, ...columnDefsFromArr(cols)];
setColumnDefs(finalColDefs);
};
const validateForm = (data) => {
if (data.reid.trim() === "") {
return { status: false, msg: "Email/Email Id is reqd" };
} else {
return { status: true };
}
};
return (
<div className="container-fluid">
<div>
<h5>Get Sessions Information</h5>
</div>
<div className="card mt-2 p-3 bg-red shadow p-3 mb-5 bg-white rounded">
<div className="row m-2">
<div className="col-sm-6">
<input
type="text"
className="form-control"
value={email || ""}
placeholder="Email / Email ID"
onChange={handleEmailChange}
/>
</div>
<div className="col-sm-2">
<button className="button btn-primary" onClick={handleGetSession}>
Get Information
</button>
</div>
</div>
</div>
{rowData == null ? null : (
<div className="card mt-2 p-3 bg-red shadow p-3 mb-5 bg-white rounded">
<div
className="ag-theme-balham"
style={{ height: "500px", width: "100%" }}
>
<AgGridReact
defaultColDef={defaultColDef}
columnDefs={columnDefs}
rowData={rowData}
frameworkComponents={frameworkComponents}
></AgGridReact>
</div>
</div>
)}
</div>
);
}
export { Sessions };
ButtonRenderer.js
import React from "react";
function ButtonRenderer(params) {
const refresh = (param) => {
return true;
};
const onClick = ($event) => {
if (params.onClick instanceof Function) {
const retParams = {
event: $event,
rowData: params.node.data,
};
params.onClick(retParams);
}
};
return (
<button className="button btn-primary" onClick={onClick}>
{params.label}
</button>
);
}
export { ButtonRenderer };
DropDownRenderer.js
import React, { useState } from "react";
function DropDownRender(params) {
const [selection, setSelection] = useState(params.dropDown[0]);
const refresh = (param) => {
return true;
};
const handleDropDown = (e) => {
setSelection(e.target.value);
};
const onClick = ($event) => {
if (params.onClick instanceof Function) {
const retParams = {
event: $event,
rowData: params.node.data,
selection: selection,
};
params.onClick(retParams);
}
};
return (
<div className="row">
<div className="col">
<select className="form-control" onChange={handleDropDown}>
{params.dropDown.map((i) => {
return (
<option key={i} value={i}>
{i}
</option>
);
})}
</select>
</div>
<div className="col">
<button className="button btn-primary" onClick={onClick}>
{params.label}
</button>
</div>
</div>
);
}
export { DropDownRender };

How to modify a specific component of a list of component rendered using map in react?

I have a PostList component with an array of posts objects. I am rendering this list of post using another pure functional component Post using Array.map() method. Post component has another component - LikeButton to like or unlike a post. Now I want to show a spinner during like or unlike on top of that LikeButton component. LikeButton Component looks something like this:
const LikeButton = (props) => {
const likeBtnClasses = [classes.LikeBtn];
const loggedInUserId = useSelector((state) => state.auth.user.id);
const isLoading = useSelector((state) => state.post.loading);
const isPostLiked = props.post.likes.find(
(like) => like.user === loggedInUserId
);
const [isLiked, setLike] = useState(isPostLiked ? true : false);
const token = useSelector((state) => state.auth.token);
const dispatch = useDispatch();
if (isLiked) {
likeBtnClasses.push(classes.Highlight);
}
const postLikeHandler = () => {
if (!isLiked) {
setLike(true);
dispatch(actions.likePost(props.post._id, token));
} else {
setLike(false);
dispatch(actions.unlikePost(props.post._id, token));
}
};
return isLoading ? (
<Spinner />
) : (
<button
className={likeBtnClasses.join(" ")}
onClick={() => postLikeHandler()}
>
<i class="far fa-thumbs-up"></i>
<small>{props.post.likes.length}</small>
</button>
);
};
Instead of showing the spinner to that single post, I am seeing it on all the posts.
My Post component looks like this:
const Post = (props) => {
return (
<div className={classes.Post}>
<div className={classes.Author}>
<img src={props.postData.avatar} alt="avatar" />
<div className={classes.AuthorDetails}>
<h3>{props.postData.name}</h3>
</div>
</div>
<div className={classes.PostText}>
<p>{props.postData.text}</p>
</div>
<hr />
<div className={classes.PostTools}>
<LikeButton post={props.postData} />
<div className={classes.PostBtn}>
<i class="far fa-comments"></i>
<small>3</small>
</div>
<div className={classes.PostBtn}>
<i class="fas fa-share"></i>
<small>2</small>
</div>
</div>
</div>
);
};
PostList component:
class PostList extends React.Component {
state = {
posts: [
{
text: "POST1",
user: "XYZ",
name: "XYZ",
id: "post1",
likes: [],
},
{
text: "POST2",
user: "johndoe#test.com",
name: "John Doe",
id: "post2",
likes: [],
},
],
};
componentDidMount() {
if (this.props.token) {
this.props.onFetchPosts(this.props.token);
this.props.onFetchUserAuthData(this.props.token);
}
}
render() {
let posts = null;
if (this.props.posts.length === 0) {
posts = this.state.posts.map((post) => {
return <Post key={post.id} postData={post} />;
});
} else {
posts = this.props.posts.map((post) => {
return <Post key={post._id} postData={post} />;
});
}
return (
<div>
<CreatePost />
{posts}
</div>
);
}
}
const mapStateToProps = (state) => {
return {
token: state.auth.token,
posts: state.post.posts,
loading: state.post.loading,
error: state.post.err,
};
};
const mapDispatchToProps = (dispatch) => {
return {
onFetchPosts: (token) => dispatch(actions.fetchPosts(token)),
onFetchUserAuthData: (token) => dispatch(actions.fetchUser(token)),
};
};
Please do some change in your to checking like/unlike is loading or not for the LikeButton.
const LikeButton = (props) => {
....
const [isButtonLoading, setButtonLoading] = useState(false);
...
return isButtonLoading ? (
<Spinner />
) : (
<button
className={likeBtnClasses.join(" ")}
onClick={() => postLikeHandler();setButtonLoading(true)}
>
<i class="far fa-thumbs-up"></i>
<small>{props.post.likes.length}</small>
</button>
);
};
Then on your dispatch callback need to set the isButtonLoading value to false.
const buttonCallback() {
// here we need to reset our flag
setButtonLoading(false);
}
const postLikeHandler = () => {
if (!isLiked) {
setLike(true);
// for this action you need to create third parameter called as callback so after response our buttonCallback will call
dispatch(actions.likePost(props.post._id, token, buttonCallback));
} else {
setLike(false);
// for this action you need to create third parameter called as callback so after response our buttonCallback will call
dispatch(actions.unlikePost(props.post._id, token, buttonCallback);
}
};
fore more details please check here.
Hope this will help you.

Categories

Resources