Ionic Capacitor App eventlistner plugin called multiple times in a single click - javascript

I am using Ionic capacitor to build my reactjs Web application. I have added a Capacitor App Apis to get the back event capture in my App.js file like this,
APPS.addListener("backButton", () => {
if (renderedScreenUIList.length === 0) {
// APPS.exitApp();
alert("exit app");
} else {
alert("previous screen");
}
});
But when i press back button this back event is called 3 times instead of one. my App.js file is.
// import "./StyleSeets/LightTheme.css";
import "./StyleSeets/theme.css";
import "./App.css";
import React, { useEffect, useState } from "react";
import Drawer from "#mui/material/Drawer";
import { App as APPS } from "#capacitor/app";
import Login from "./Components/Login";
import ERPUtils from "./Components/ERPUtils";
import Main from "./Components/Main";
// ************************** back fire event handling ****************
const renderedScreenUIList = [];
function App() {
// const [ stylePath, setStylePath ] = useState("./StyleSeets/LightTheme.css");
const [renderPageType, setRenderPageType] = useState("login");
const [isMobileView, setIMobileView] = useState(window.innerWidth < 920);
const isConsoleOpen = false;
window.onresize = function () {
console.log(window.outerHeight - window.innerHeight);
if ((window.outerHeight - window.innerHeight) > 100) { console.log("open"); } else console.log("close");
};
const themeList = [
{ value: "#2776ed", label: "light" },
{ value: "#45b11c", label: "nature" },
{ value: "#2AB67B", label: "evening" },
{ value: "#add8e6", label: "sky" },
{ value: "#2b035b", label: "dark" }];
const languageList = [
{ value: "ENG", label: "English" },
{ value: "MAL", label: "മലയാളം" },
{ value: "HIND", label: "हिन्दी" }];
const [language, setLanguage] = useState("ENG");
const onThemeChangeCall = () => {
const themeName = sessionStorage.getItem("theme-name");
const themeElement = document.getElementById("app-theme");
themeElement.className = "App";
document.documentElement.setAttribute("data-theme", themeName);
};
APPS.addListener("backButton", () => {
if (renderedScreenUIList.length === 0) {
// APPS.exitApp();
alert("exit app");
} else {
alert("previous screen");
}
});
const languageChanged = (type) => {
setLanguage(type);
};
useEffect(() => {
onThemeChangeCall();
}, []);
const changePage = (page) => {
setRenderPageType(page);
};
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
const changeTheme = (type) => {
sessionStorage.setItem("theme-name", type);
document.documentElement.classList.add("color-theme-in-transition");
window.setTimeout(() => {
document.documentElement.classList.remove("color-theme-in-transition");
}, 1000);
onThemeChangeCall();
};
const toggleDrawer = (value) => {
setIsSettingsOpen(value);
};
const changeLanguage = (type) => {
sessionStorage.setItem("language-theme", type);
languageChanged(type);
};
useEffect(() => {
setIMobileView(window.innerWidth < 920);
}, [window.innerWidth]);
return (
<div className="App" id="app-theme">
{renderPageType === "login"
? (
<Login
onThemeChangeCall={onThemeChangeCall}
changePage={changePage}
languageChanged={languageChanged}
toggleDrawer={toggleDrawer}
/>
)
: null}
{renderPageType === "home"
? (
<Main
onThemeChangeCall={onThemeChangeCall}
changePage={changePage}
languageChanged={languageChanged}
toggleDrawer={toggleDrawer}
isMobileView={isMobileView}
/>
)
: null}
<Drawer
anchor="right"
open={isSettingsOpen}
onClose={() => toggleDrawer(false)}
>
<div className="p-2 " style={{ width: "250px", backgroundColor: "var(--primaryBackground)" }}>
<h6 className="border-bottom p-2">Themes</h6>
<div className="">
{!ERPUtils.isNullorWhiteSpace(themeList) ? themeList.map((el, index) => (
<div
key={index}
onKeyDown={(e) => {
if (ERPUtils.isKeyBoardEnterPressed(e)) {
changeTheme(el.label);
}
}}
role="button"
tabIndex={0}
className="p-2 d-flex align-items-center font-2 justify-content-start"
onClick={() => changeTheme(el.label)}
>
<span className="theme-thumbnail" style={{ backgroundColor: el.value }} />
<span>{(`${el.label}`).toLocaleUpperCase()}</span>
</div>
)) : null}
</div>
</div>
<div className="p-2" style={{ width: "250px", backgroundColor: "var(--primaryBackground)" }}>
<h6 className="border-bottom p-2">Language</h6>
<div className="">
{!ERPUtils.isNullorWhiteSpace(languageList) ? languageList.map((el, index) => (
<div
key={index}
onKeyDown={(e) => {
if (ERPUtils.isKeyBoardEnterPressed(e)) {
changeLanguage(el.value);
}
}}
role="button"
tabIndex={0}
className="p-2 d-flex align-items-center font-2 justify-content-start"
onClick={() => changeLanguage(el.value)}
>
<span className="language-thumbnail">{el?.label.split("")[0]}</span>
<span>{(`${el.label}`).toLocaleUpperCase()}</span>
</div>
)) : null}
</div>
</div>
</Drawer>
{/* <ThemeSwitcherProvider defaultTheme="light" themeMap={themes}> */}
{/* <link rel="stylesheet" type="text/css" href={stylePath} /> */}
{/* </ThemeSwitcherProvider> */}
</div>
);
}
export default App;
Kindly suggest a solution for this problem ?. If I call this event listener outside App.js file it only calls once.

With each page reload the listener is added again. To fix this you should call removeAllListeners() => Promise<void> once at the beginning:
App.removeAllListeners().then(() => {
App.addListener("backButton", () => {
if (renderedScreenUIList.length === 0) {
// APPS.exitApp();
alert("exit app");
} else {
alert("previous screen");
}
});
});

Related

React problem Cannot read properties of undefined (reading 'map') in a quiz app?

I am working on a quiz project. Particularly I am working on selecting an answer out of 4 options. Whenever I click on an option I am encountering an error which is given below
This is my App.js file
import { useEffect, useState } from "react";
import Quiz from "./components/Quiz";
import { nanoid } from "nanoid";
function App() {
const [showScreen, setShowScreen] = useState(false);
const [quizData, setQuizData] = useState([]);
useEffect(() => {
fetch("https://opentdb.com/api.php?amount=5&category=21&type=multiple")
.then((res) => res.json())
.then((data) =>
setQuizData(
data.results.map((question) => {
return {
...question,
id: nanoid(),
answers: shuffle([
...question.incorrect_answers,
question.correct_answer,
]),
correct_answer: question.correct_answer,
};
})
)
);
}, []);
function shuffle(arr) {
let array = arr.map((ans) => {
return {
id: nanoid(),
answer: ans,
isSelected: false,
};
});
for (let i = array.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
let temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
function handleSelect(id, selectedAnsId) {
console.log(id, selectedAnsId);
setQuizData((prevQuizData) => {
console.log(quizData);
prevQuizData.map((question) => {
return question.id === id
? {
...question,
answers: question.answers.map((answer) => {
return answer.id === selectedAnsId
? {
...answer,
isSelected: !answer.isSelected,
}
: { ...answer, isSelected: false };
}),
}
: question;
});
});
}
const newQuizData = quizData.map((question) => {
return (
<Quiz
key={question.id}
id={question.id}
question={question.question}
answers={question.answers}
handleSelect={handleSelect}
/>
);
});
function openSeparateScreen() {
setShowScreen((prevShowScreen) => !prevShowScreen);
}
return (
<div className="App">
{showScreen ? (
<div className="quiz-container">
{newQuizData}
<button
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
margin: "auto",
}}
className="btn"
>
Check answers
</button>
</div>
) : (
<div className="container">
<h1 className="heading">Quizzical</h1>
<p className="text">To start a quiz please click on the button</p>
<button className="btn" onClick={openSeparateScreen}>
Start Quiz
</button>
</div>
)}
</div>
);
}
export default App;
This is my Quiz.js file
import React from "react";
const Quiz = (props) => {
return (
<>
<div className="questions">
<h3>{props.question}</h3>
<div className="options">
{props.answers.map((answer) => {
return (
<h6
key={answer.id}
className={`${
answer.isSelected ? "isSelected" : "transparent"
}`}
onClick={() => props.handleSelect(props.id, answer.id)}
>
{answer.answer}{" "}
</h6>
);
})}
</div>
</div>
<hr />
</>
);
};
export default Quiz;
The error message shows I am getting the error at line 66 which starts from
const newQuizData = quizData.map((question) => {
return (
<Quiz
key={question.id}
id={question.id}
question={question.question}
answers={question.answers}
handleSelect={handleSelect}
/>
);
});
any kind of help would be much appreciated.
Your quizData is undefined at some point.
The most likely culprit is
setQuizData((prevQuizData) => {
console.log(quizData);
prevQuizData.map((question) => {
you don't return the value there; try to change it to return prevQuizData.map((question) => {

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 };

React context "update" not working properly

I'm making custom tab component and I have issue on closing tab. Switching tab is working ok but when I close last tab I want to set active previouse tab and this is not working.
setTabs (context update) is updating array "data" in object but not "activeTab".
I'm using react Context to store list of tabs and active tab.
Switching tabs is working correctly, opening new tab also works fine.
TabsContext
import React, { useReducer, useEffect } from "react";
const initialState = {
data: [
{
name: "Start",
component: "StartTab",
cantClose: true,
params: null
}
],
activeTab: 0
};
const localState = JSON.parse(localStorage.getItem("tabs"));
const TabsContext = React.createContext();
let reducer = (tabs, newTabs) => {
if (newTabs === null) {
localStorage.removeItem("tabs");
return initialState;
}
return { ...tabs, ...newTabs };
};
function TabsProvider(props) {
const [tabs, setTabs] = useReducer(reducer, localState || initialState);
useEffect(() => {
localStorage.setItem("tabs", JSON.stringify(tabs));
}, [tabs]);
return (
<TabsContext.Provider value={{ tabs, setTabs }}>
{props.children}
</TabsContext.Provider>
);
}
export { TabsContext, TabsProvider };
MainTabs Component:
import React, { Component, useContext } from "react";
import { TabsContext } from "../../providers/TabsProvider";
import StartTab from "./tab.start";
...
class TabComponent extends Component {
components = {
StartTab: StartTab,
...
};
render() {
const TagName = this.components[this.props.tag];
return <TagName />
}
}
const TabsMain = () => {
const { tabs, setTabs } = useContext(TabsContext);
const closeTab = (index) => {
tabs.data.splice(index, 1);
if (tabs.activeTab == tabs.data.length ) {
tabs.activeTab--;
}
setTabs({ data: tabs.data, activeTab: tabs.activeTab });
};
const tabsNavigation = tabs.data.map((tab, index) =>
<li key={index}>
<button
onClick={() => {
setTabs({ data: tabs.data, activeTab: index });
}}
className={`${tabs.activeTab == index ? 'active' : ''}`}
>
{tab.name}
<div onClick={() => {
closeTab(index);
}} className={`close_button ${!tab.cantClose ? 'show' : 'hide'}`}>X</div>
</button>
</li>
);
const tabsPanels = tabs.data.map((tab, index) =>
<div key={index} className={`panel ${tabs.activeTab == index ? 'active' : ''}`}>
<TabComponent tag={tab.component} />
</div>
);
return (
<div className="tabs">
<ul className="tabs__navigation">
{tabsNavigation}
</ul>
<div className="tabs__content">
{tabsPanels}
</div>
</div>
);
};
export default TabsMain;
Navigation Component
import React, { Component, useContext } from "react";
import { TabsContext } from "../../providers/TabsProvider";
const Navigation = () => {
const { tabs, setTabs } = useContext(TabsContext);
const openTab = (newTab) => {
tabs.data.push(newTab);
setTabs(tabs);
};
return (
<ul className="navigation">
<li>
<button onClick={() => { openTab({ name: "Start", component: "StartTab" }); }}>
Start
</button>
</li>
</ul>
);
};
export default Navigation;
I found the solution.
First of all I have double click action:
1) on click on tab - selectTab,
2) on "X" on same button, when i clicked X then selectTab and closeTab are fired (setTabs was fired also two times)
The solution Was to extract "X" from button - from this:
<button
onClick={() => {
setTabs({ data: tabs.data, activeTab: index });
}}
className={`${tabs.activeTab == index ? 'active' : ''}`}
>
{tab.name}
<div onClick={() => {
closeTab(index);
}} className={`close_button ${!tab.cantClose ? 'show' : 'hide'}`}>X</div>
</button>
to this:
<button
onClick={() => {
setTabs({ data: tabs.data, activeTab: index });
}}
className={`${tabs.activeTab == index ? 'active' : ''}`}
>
{tab.name}
</button>
<div onClick={() => {
closeTab(index);
}} className={`close_button ${!tab.cantClose ? 'show' : 'hide'}`}>X</div>
and change closeTab function to this:
const closeTab = (index) => {
tabs.data.splice(index, 1);
setTabs({ data: tabs.data, activeTab: index-1 });
};

Why isn't this button showing when the state is false?

I have created a component to function as a "Like/Unlike" button. When the state is true, the "Unlike" button successfully displays, but when I click "Unlike", and it DOES unlike successfully, the state should be set to false as (liked: false). However, I don't see the button.
One thing I noticed is, when I click "Unlike", the "Unlike" button disappears and the "Like" button does appear, for a millisecond, and then it vanishes in thin air. I cannot figure it out why.
Here are all the codes for my like button component:
import React from "react";
import { API, graphqlOperation } from "aws-amplify";
import { Button } from "element-react";
import { createLike, deleteLike } from "../graphql/mutations";
import { UserContext } from "../App";
class Like extends React.Component {
state = {
liked: "",
};
componentDidMount() {
this.setLiked();
}
setLiked() {
console.log(this.props);
const { user } = this.props;
const { post } = this.props;
if (post.likes.items.find((items) => items.liker === user.username)) {
this.setState({ liked: true });
console.log("liked: true");
} else {
this.setState({ liked: false });
console.log("liked: false");
}
}
handleLike = async (user) => {
try {
const input = {
liker: user.username,
likePostId: this.props.postId,
};
await API.graphql(graphqlOperation(createLike, { input }));
this.setState({
liked: true,
});
console.log("Liked!");
} catch (err) {
console.log("Failed to like", err);
}
};
handleUnlike = async (likeId) => {
try {
const input = {
id: likeId,
};
await API.graphql(graphqlOperation(deleteLike, { input }));
this.setState({
liked: false,
});
console.log("Unliked!");
} catch (err) {
console.log("Failed to unlike", err);
}
};
render() {
const { like } = this.props;
const { liked } = this.state;
return (
<UserContext.Consumer>
{({ user }) => (
<React.Fragment>
{liked ? (
<Button type="primary" onClick={() => this.handleUnlike(like.id)}>
Unlike
</Button>
) : (
<Button
type="primary"
onClick={() => this.handleLike(user, like.id)}
>
Like
</Button>
)}
</React.Fragment>
)}
</UserContext.Consumer>
);
}
}
export default Like;
The code of the parent component:
import React from "react";
import { API, graphqlOperation } from "aws-amplify";
import {
onCreateComment,
onCreateLike,
onDeleteLike,
} from "../graphql/subscriptions";
import { getPost } from "../graphql/queries";
import Comment from "../components/Comment";
import Like from "../components/Like";
import LikeButton from "../components/LikeButton";
import { Loading, Tabs, Icon } from "element-react";
import { Link } from "react-router-dom";
import { S3Image } from "aws-amplify-react";
import NewComment from "../components/NewComment";
class PostDetailPage extends React.Component {
state = {
post: null,
isLoading: true,
isAuthor: false,
};
componentDidMount() {
this.handleGetPost();
this.createCommentListener = API.graphql(
graphqlOperation(onCreateComment)
).subscribe({
next: (commentData) => {
const createdComment = commentData.value.data.onCreateComment;
const prevComments = this.state.post.comments.items.filter(
(item) => item.id !== createdComment.id
);
const updatedComments = [createdComment, ...prevComments];
const post = { ...this.state.post };
post.comments.items = updatedComments;
this.setState({ post });
},
});
this.createLikeListener = API.graphql(
graphqlOperation(onCreateLike)
).subscribe({
next: (likeData) => {
const createdLike = likeData.value.data.onCreateLike;
const prevLikes = this.state.post.likes.items.filter(
(item) => item.id !== createdLike.id
);
const updatedLikes = [createdLike, ...prevLikes];
const post = { ...this.state.post };
post.likes.items = updatedLikes;
this.setState({ post });
},
});
this.deleteLikeListener = API.graphql(
graphqlOperation(onDeleteLike)
).subscribe({
next: (likeData) => {
const deletedLike = likeData.value.data.onDeleteLike;
const updatedLikes = this.state.post.likes.items.filter(
(item) => item.id !== deletedLike.id
);
const post = { ...this.state.post };
post.likes.items = updatedLikes;
this.setState({ post });
},
});
}
componentWillUnmount() {
this.createCommentListener.unsubscribe();
}
handleGetPost = async () => {
const input = {
id: this.props.postId,
};
const result = await API.graphql(graphqlOperation(getPost, input));
console.log({ result });
this.setState({ post: result.data.getPost, isLoading: false }, () => {});
};
checkPostAuthor = () => {
const { user } = this.props;
const { post } = this.state;
if (user) {
this.setState({ isAuthor: user.username === post.author });
}
};
render() {
const { post, isLoading } = this.state;
return isLoading ? (
<Loading fullscreen={true} />
) : (
<React.Fragment>
{/*Back Button */}
<Link className="link" to="/">
Back to Home Page
</Link>
{/*Post MetaData*/}
<span className="items-center pt-2">
<h2 className="mb-mr">{post.title}</h2>
</span>
<span className="items-center pt-2">{post.content}</span>
<S3Image imgKey={post.file.key} />
<div className="items-center pt-2">
<span style={{ color: "var(--lightSquidInk)", paddingBottom: "1em" }}>
<Icon name="date" className="icon" />
{post.createdAt}
</span>
</div>
<div className="items-center pt-2">
{post.likes.items.map((like) => (
<Like
user={this.props.user}
like={like}
post={post}
postId={this.props.postId}
/>
))}
</div>
<div className="items-center pt-2">
{post.likes.items.length}people liked this.
</div>
<div>
Add Comment
<NewComment postId={this.props.postId} />
</div>
{/* Comments */}
Comments: ({post.comments.items.length})
<div className="comment-list">
{post.comments.items.map((comment) => (
<Comment comment={comment} />
))}
</div>
</React.Fragment>
);
}
}
export default PostDetailPage;
I think I know why it doesn't show up. It's because at first when the user hasn't liked it, there is no "like" object, so there is nothing to be shown, as it is only shown when there is a "like" mapped to it. I don't know how to fix it though.

How i can replace states ( arrays ) ReactJs

today I offer you a new challenge
my problem and the next
in the following code
I map objects in my database
I also map a list of items
and so basically I would like to pass my database and not the Items table
basically I want to do exactly the same thing as in the following code except that instead of using the items array, I would like to be able to use the data array which contains my database
do you have any idea how to do this?
I hope I was precise thanks to you for the help Neff
ps: Sorry for the length of the code i try to do my best to clean a little
import React, { Component } from 'react';
import { CardText, Card,Row, Col, Button } from 'reactstrap';
import axios from 'axios'
import GridLayout from 'react-grid-layout';
import SmsForm from './Sms/SMSForm'
import FruitList from './FruitList'
import './AdminPage.scss'
const UP = -1;
const DOWN = 1;
const entrypoint = process.env.REACT_APP_API_ENTRYPOINT+'/api';
class AdminPage extends Component {
constructor(props) {
super(props);
this.state = {
items: [
{ id: 1, name: "orange", bgColor: "#f9cb9c" },
{ id: 2, name: "lemon", bgColor: "#fee599" },
{ id: 3, name: "strawberry", bgColor: "#e06666" }
],
data: [],
}
handleMove = (id, direction) => {
const { items } = this.state;
const position = items.findIndex(i => i.id === id);
if (position < 0) {
throw new Error("Given item not found.");
} else if (
(direction === UP && position === 0) ||
(direction === DOWN && position === items.length - 1)
) {
return; // canot move outside of array
}
const item = items[position]; // save item for later
const newItems = items.filter(i => i.id !== id); // remove item from array
newItems.splice(position + direction, 0, item);
this.setState({ items: newItems });
};
// rest of the component
onHandleChange(event) {
const name = event.target.getAttribute('name');
this.setState({
message: { ...this.state.message, [name]: event.target.value }
});
}
getRandom = async () => {
const res = await axios.get(
entrypoint + "/alluserpls"
)
this.setState({ data: res.data })
}
componentDidMount() {
this.getRandom()
}
render() {
let datas = this.state.data.map(datass => {
const status = JSON.parse(localStorage.getItem("validated-order") || "{}")[datass.id];
return (
<div>
< Col sm="12" key={datass.id} className="wtfFuHereIsForOnlyBackGroundColorForCol12Nice">
<FruitList fruitList={this.state.items} onMove={this.handleMove} />
<GridLayout className="GridlayoutTextOnlyForGridOuiAndHeigthbecauseHeigthWasBug" layout={layout} cols={12} rowHeight={30} width={1200}>
<div key="a">
<Card body className="yoloCardBodyForbackGroundAndRaduisBorderForAdminPageWtf">
<CardText className="cardTextForAdminPageForDataName"> Commande de {datass.name}</CardText>
</Card>
</div>
</ Col>
</div>
)
})
return (
<div> <div>
<Row className="wtfHereIsAgainOnlyForRowAndMarginForAdminPageJustWtf">
<div className="isJustForOnlyPaddingOnRowForAdminPage" >
<div className="navBarGridMenuAdminPage">
<div className="thatIsOnlyForSpaceArroundOnDivAdminPage">
<CardText className="maybeColForAdminPageOuiOui"> Nouvelles commandes </CardText>
</div>
</div>
<div>
{datas}
</div>
</div>
</Row>
</div>
<div className="box">
</div>
</div>
)
}
}
export default AdminPage
here my second components
import React from "react";
const LEFT = -1;
const RIGHT = 1;
class FruitList extends React.Component {
render() {
const { fruitList, onMove } = this.props;
return (
<div style={{ display: "flex" }}>
{fruitList.map(item => (
<div
key={item.id}
style={{
backgroundColor: item.bgColor,
display: "flex"
}}
>
<div className="fruitsArrows">
<a onClick={() => onMove(item.id, LEFT)}>←</a>
</div>
<div className="fruitsId">{item.id}</div>
<div className="fruitsName">{item.name}</div>
<div className="fruitsArrows">
<a onClick={() => onMove(item.id, RIGHT)}>→</a>
</div>
</div>
))}
</div>
);
}
}
export default FruitList;
To delete particular list do like this-
pass your item(object).
<a onClick={() => onMove(item)}>→</a>
handleMove function
handleMove = (row) => {
let filtered = this.state.items.filter(item=>item.id!==row.id);
this.setState({ items: filtered});
};

Categories

Resources