here I have a case where when I click a link "create custom url" it will change to "cancel" and "save".
for example like this :
When I click "create custom url"
Turned into like this
Want it like that.
However, when I try to make it this is the result
Cancel and save together, cancel and save should have its own function.
This is my code:
const ShareKey = ({ surveyId, shareKey = "", setShareKey, refProps }) => {
const [isLoading, setIsLoading] = useState(false);
const [customInput, setCustomInput] = useState(process.env.NEXT_PUBLIC_SHARE_URL + "/");
const [active, setActive] = useState(false);
const [show, setShow] = useState(false);
const getLink = () => {
setShareKey("");
setIsLoading(true);
automaticApproval({ surveyId })
.then((resolve) => {
console.log(resolve);
setShareKey(resolve.key);
})
.catch((reject) => {
console.log(reject);
toast.error(reject);
})
.finally(() => {
setIsLoading(false);
});
};
return (
<section>
<button onClick={getLink} disabled={isLoading} className={styles.link_button}>
{`GENERATE ${shareKey && "NEW "}LINK`}
</button>
{shareKey && (
<div className={styles.link_container}>
<label>Share link</label>
<div className={styles.link}>
<input
value={active ? customInput : process.env.NEXT_PUBLIC_SHARE_URL + "/" + shareKey}
onChange={(e) => {
setCustomInput(e.target.value);
}}
disabled={!active}
ref={refProps}
/>
{!show &&
<button
onClick={() => {
if (typeof navigator !== "undefined") {
navigator.clipboard.writeText(refProps.current.value);
toast.info("Copied to clipboard");
}
}}
>
copy
</button>
}
</div>
<p className={styles.custom} onClick={() => {setActive(!active); setShow(!show);}}>
{active ? "Cancel " + " Save" : "Create Custom URL"}
</p>
</div>
)}
</section>
);
};
Is there something wrong in my code? Please help. Thank you
This is how you gonna do it.
Also I changed your <p> to <div>. You can't nest anything inside of it.
You can change the container <div> to React Fragment if you don't want have some additional div.
You also have to fix the style.
const ShareKey = ({ surveyId, shareKey = "", setShareKey, refProps }) => {
const [isLoading, setIsLoading] = useState(false);
const [customInput, setCustomInput] = useState(
process.env.NEXT_PUBLIC_SHARE_URL + "/"
);
const [active, setActive] = useState(false);
const [show, setShow] = useState(false);
const getLink = () => {
setShareKey("");
setIsLoading(true);
automaticApproval({ surveyId })
.then((resolve) => {
console.log(resolve);
setShareKey(resolve.key);
})
.catch((reject) => {
console.log(reject);
toast.error(reject);
})
.finally(() => {
setIsLoading(false);
});
};
const toggleFunction = () => {
setActive(!active);
setShow(!show);
};
return (
<section>
<button
onClick={getLink}
disabled={isLoading}
className={styles.link_button}
>
{`GENERATE ${shareKey && "NEW "}LINK`}
</button>
{shareKey && (
<div className={styles.link_container}>
<label>Share link</label>
<div className={styles.link}>
<input
value={
active
? customInput
: process.env.NEXT_PUBLIC_SHARE_URL + "/" + shareKey
}
onChange={(e) => {
setCustomInput(e.target.value);
}}
disabled={!active}
ref={refProps}
/>
{!show && (
<button
onClick={() => {
if (typeof navigator !== "undefined") {
navigator.clipboard.writeText(refProps.current.value);
toast.info("Copied to clipboard");
}
}}
>
copy
</button>
)}
</div>
{active ? (
<div>
<button onClick={toggleFunction}>Cancel</button>
<button onClick={yourSaveFunction}>Save</button>
</div>
) : (
<div className={styles.custom} onClick={toggleFunction}>
Create Custom URL
</div>
)}
</div>
)}
</section>
);
};
Related
I'm having a problem when I input a new url in the project's custom url.
So this url input can be edited, now when it can be edited I enter a new url behind "https://www.kutanya.com/share/", then I save and a notification "custom url is exist" appears.
my API =
export const setCustomUrl = (args, surveyId) => {
return new Promise((resolve, reject) => {
axios
.put(process.env.NEXT_PUBLIC_API_URL + `/v1/smart-survey/${surveyId}/custom-url`, args, {
headers: { Authorization: `Bearer ${token()}` },
})
.then((response) => {
resolve(response.data.data);
})
.catch((error) => {
console.log(error.response);
reject(error?.response?.data?.message || "Network error.");
});
});
};
file .jsx =
const ShareKey = ({ customUrl, surveyId, shareKey = "", setShareKey, updateUrl, refProps }) => {
const [isLoading, setIsLoading] = useState(false);
const [customInput, setCustomInput] = useState(process.env.NEXT_PUBLIC_SHARE_URL + "/");
const [active, setActive] = useState(false);
const [show, setShow] = useState(false);
const router = useRouter();
const getLink = () => {
setShareKey("");
setIsLoading(true);
automaticApproval({ surveyId })
.then((resolve) => {
console.log(resolve);
setShareKey(resolve.key);
})
.catch((reject) => {
console.log(reject);
toast.error(reject);
})
.finally(() => {
setIsLoading(false);
});
};
const toggleFunction = () => {
setActive(!active);
setShow(!show);
};
const SaveFunction = () => {
setCustomUrl({ customUrl }, router.query.surveyId)
.then((resolve) => {
console.log(resolve);
updateUrl({
customUrl
});
setShareKey(resolve.key);
toast.info("Berhasil ubah link");
})
.catch((reject) => {
console.log(reject);
toast.error(reject);
})
.finally(() => {
setIsLoading(false);
});
};
return (
<section>
<button onClick={getLink} disabled={isLoading} className={styles.link_button}>
{`GENERATE ${shareKey && "NEW "}LINK`}
</button>
{shareKey && (
<div className={styles.link_container}>
<label>Share link</label>
<div className={styles.link}>
<input
value={active ? customInput : process.env.NEXT_PUBLIC_SHARE_URL + "/" + shareKey}
onChange={(e) => {
setCustomInput(e.target.value);
}}
disabled={!active}
ref={refProps}
/>
{!show && (
<button
onClick={() => {
if (typeof navigator !== "undefined") {
navigator.clipboard.writeText(refProps.current.value);
toast.info("Copied to clipboard");
}
}}
>
copy
</button>
)}
</div>
{active ? (
<div className={styles.custom2}>
<button style={{ color: "red", marginTop: "6px" }} onClick={toggleFunction}>
Cancel
</button>
<button style={{ color: "blue", marginTop: "6px" }} onClick={SaveFunction}>
Save
</button>
</div>
) : (
<div className={styles.custom} onClick={toggleFunction}>
Create Custom URL
</div>
)}
</div>
)}
</section>
);
};
The question is, is there something wrong in my coding so that the output is 400 in the console and the notification appears?
Every video I've seen regarding to showing or hiding a div, is quite not effective at all if you're making use of a state that's based on true or false, thus when a button is clicked through the .map() all elements that are hidden would be shown, therefore it wouldn't be in great use of all, I guess that's why the element's index should be in use to determine which element should shown or hidden right?
Scenario
So I'm building a social platform for a learning experience, where I map through all my posts in an array, once I click my comment Icon, the comments should be shown for that post, but unfortunately I'm unable to find a solution regarding to the use of functional components.
this is what I have:
import React, { useState, useEffect, useReducer, useRef, useMemo } from "react";
import axios from "axios";
import Cookies from "universal-cookie";
import "../../styles/private/dashboard.css";
import DashboardHeader from "../../components/private/templates/header";
import DashboardSidebar from "../../components/private/templates/sidebar";
import ImageSearchIcon from "#material-ui/icons/ImageSearch";
import VideoLibraryIcon from "#material-ui/icons/VideoLibrary";
import FavoriteIcon from "#material-ui/icons/Favorite";
import SendIcon from "#material-ui/icons/Send";
import { Avatar } from "#material-ui/core";
import { useSelector, useDispatch } from "react-redux";
import { newPost } from "../../redux/actions/posts/new-post";
import { likePost } from "../../redux/actions/posts/like-post";
import { getPosts } from "../../redux/actions/posts/get-posts";
import { unlikePost } from "../../redux/actions/posts/unlike-post";
import { getPostLikes } from "../../redux/actions/posts/get-likes";
import { likePostComment } from "../../redux/actions/posts/like-comment";
import { unlikePostComment } from "../../redux/actions/posts/unlike-comment";
import { newPostComment } from "../../redux/actions/posts/new-post-comment";
import ChatBubbleOutlineIcon from "#material-ui/icons/ChatBubbleOutline";
import LoopIcon from "#material-ui/icons/Loop";
import FavoriteBorderIcon from "#material-ui/icons/FavoriteBorder";
import MoreHorizIcon from "#material-ui/icons/MoreHoriz";
import Pusher from "pusher-js";
import FlipMove from "react-flip-move";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import io from "socket.io-client";
const socket = io.connect("http://localhost:5000");
function Dashboard({
history,
getPost,
getLike,
getAllPosts,
getAllLikes,
likePosts,
unlikePosts,
}) {
const [participants, setParticipants] = useState({});
const cookies = new Cookies();
const [toggle, setToggle] = useState(false);
const [messages, setMessages] = useState("");
const [media, setMedia] = useState(null);
const [posts, setPosts] = useState([]);
const [error, setError] = useState("");
const [comment, setComment] = useState();
const userLogin = useSelector((state) => state.userLogin);
const { user } = userLogin;
const [uname, setUname] = useState(user.name);
const [upic, setUpic] = useState(user.pic);
const dispatch = useDispatch();
const config = {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${user.token}`,
},
};
useEffect(() => {
if (!cookies.get("authToken")) {
history.push("/login");
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [history]);
useEffect(() => {
axios.get("/api/post/posts", config).then((response) => {
setPosts(response.data);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
const handler = (item) => {
setPosts((oldPosts) => {
const findItem = oldPosts.find((post) => post._id === item._id);
if (findItem) {
return oldPosts.map((post) => (post._id === item._id ? item : post));
} else {
return [item, ...oldPosts];
}
});
};
socket.on("posts", handler);
return () => socket.off("posts", handler);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const postHandler = (e) => {
e.preventDefault();
dispatch(newPost(uname, upic, messages, media));
setMessages("");
};
const LikePost = (postId) => {
likePosts(postId, user._id, user.name, user.pic);
};
const UnlikePost = (postId) => {
unlikePosts(postId);
};
const submitComment = (postId) => {
dispatch(newPostComment(postId, uname, upic, comment));
setComment("");
};
const LikeCommentPost = (postId, commentId) => {
dispatch(likePostComment(postId, commentId, user._id, user.name, user.pic));
};
const UnlikeCommentPost = (postId, commentId) => {
dispatch(unlikePostComment(postId, commentId));
};
return error ? (
<span>{error}</span>
) : (
<div className="dashboard">
<DashboardHeader />
<div className="dashboard__container">
<div className="dashboard__sidebar">
<DashboardSidebar />
</div>
<div className="dashboard__content">
<div className="dashboard__contentLeft">
<div className="dashboard__messenger">
<div className="dashboard__messengerTop">
<Avatar src={user.pic} className="dashboard__messengerAvatar" />
<input
type="text"
placeholder={`What's on your mind, ${user.name}`}
value={messages}
onChange={(e) => setMessages(e.target.value)}
/>
<SendIcon
className="dashboard__messengerPostButton"
onClick={postHandler}
/>
</div>
<div className="dashboard__messengerBottom">
<ImageSearchIcon
className="dashboard__messengerImageIcon"
value={media}
onChange={(e) => setMedia((e) => e.target.value)}
/>
<VideoLibraryIcon className="dashboard__messengerVideoIcon" />
</div>
</div>
<div className="dashboard__postsContainer">
<FlipMove>
{posts.map((post, i) => (
<div className="dashboard__post" key={i}>
<MoreHorizIcon className="dashboard__postOptions" />
<div className="dashboard__postTop">
<Avatar
className="dashboard__postUserPic"
src={post.upic}
/>
<h3>{post.uname}</h3>
</div>
<div className="dashboard__postBottom">
<p>{post.message}</p>
{media === null ? (
""
) : (
<div className="dashboard__postMedia">{media}</div>
)}
</div>
<div className="dashboard__postActions">
{toggle ? (
<ChatBubbleOutlineIcon
onClick={() => setToggle(!toggle)}
className="dashboard__actionComment"
/>
) : (
<ChatBubbleOutlineIcon
onClick={() => setToggle(!toggle)}
className="dashboard__actionComment"
/>
)}
<label
id="totalLikes"
className="dashboard__comments"
style={{ color: "forestgreen" }}
>
{post.commentCount}
</label>
{post.likes.find((like) => like.uid === user._id) ? (
<FavoriteIcon
onClick={() => UnlikePost(post._id)}
className="dashboard__actionUnlike"
/>
) : (
<FavoriteBorderIcon
onClick={() => LikePost(post._id)}
className="dashboard__actionLike"
/>
)}
<label
id="totalLikes"
className="dashboard__likes"
style={{ color: "forestgreen" }}
>
{post.likeCount}
</label>
</div>
<div
className={
toggle
? "dashboard__commentContent toggle"
: "dashboard__commentContent"
}
>
<div className="dashboard__postComments">
{post.comments.map((comment) => (
<div
key={comment.toString()}
className="dashboard__postComment"
>
<div className="dashboard__postCommentTop">
<Avatar src={comment.upic} />
<h4>{comment.uname}</h4>
</div>
<p>{comment.message}</p>
<div className="dashboard__postCommentActions">
{comment.likes.find(
(like) => like.uid === user._id
) ? (
<FavoriteIcon
onClick={() =>
UnlikeCommentPost(post._id, comment._id)
}
className="dashboard__actionUnlike"
/>
) : (
<FavoriteBorderIcon
onClick={() =>
LikeCommentPost(post._id, comment._id)
}
className="dashboard__actionLike"
/>
)}
<label
id="totalLikes"
className="dashboard__likes"
style={{ color: "forestgreen" }}
>
{comment.likeCount}
</label>
</div>
</div>
))}
</div>
<div className="dashboard__commentInput">
<input
type="text"
placeholder="Comment post"
value={comment}
onChange={(e) => setComment(e.target.value)}
/>
<button onClick={() => submitComment(post._id)}>
Send
</button>
</div>
</div>
</div>
))}
</FlipMove>
</div>
</div>
<div className="dashboardContentRight"></div>
</div>
</div>
</div>
);
}
Dashboard.propTypes = {
getLike: PropTypes.arrayOf(PropTypes.string),
getPost: PropTypes.arrayOf(PropTypes.string),
likePost: PropTypes.arrayOf(PropTypes.string),
unlikePost: PropTypes.arrayOf(PropTypes.string),
};
function mapStateToProps(state) {
return {
getPost: getPosts(state),
getLike: getPostLikes(state),
likePosts: likePost(state),
unlikePosts: unlikePost(state),
};
}
function mapDispatchToProps(dispatch) {
return {
getAllPosts: (posts) => dispatch(getPosts(posts)),
getAllLikes: (likes) => dispatch(getPostLikes(likes)),
likePosts: (like) => dispatch(likePost(like)),
unlikePosts: (like) => dispatch(unlikePost(like)),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Dashboard);
Extra
here is a unlisted video from youtube, just for incase that you do not understand.
You have a lot of code going there... You should refactor that i smaller component, more easier to read and maintainability.
You can check there the answer i posted, it's the same core issue:
The axios delete functionality is only deleting last user from table, not the one I click on
your toggle is based on the global state, so each post doesn't have its proper way to see if it's open or not.That's why it will open everything
You need to tell which one is open and which ones aren't.
Here i refactored your code to make it work with multiple boxes open, i didn't test it on codesandbox, i let you try, but it wasn't big changes, so it should works.
Please pay attention to these news changes:
useState property openBoxes
Method toggleCommentBox
Method isCommentBoxOpen
I then replaced in your jsx the way you check if the comment box is open or not.
function Dashboard({
history,
getPost,
getLike,
getAllPosts,
getAllLikes,
likePosts,
unlikePosts,
}) {
const [participants, setParticipants] = useState({});
const cookies = new Cookies();
const [openBoxes, setOpenBoxes] = useState([]);
const [messages, setMessages] = useState("");
const [media, setMedia] = useState(null);
const [posts, setPosts] = useState([]);
const [error, setError] = useState("");
const [comment, setComment] = useState();
const userLogin = useSelector((state) => state.userLogin);
const {user} = userLogin;
const [uname, setUname] = useState(user.name);
const [upic, setUpic] = useState(user.pic);
const dispatch = useDispatch();
const config = {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${user.token}`,
},
};
useEffect(() => {
if (!cookies.get("authToken")) {
history.push("/login");
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [history]);
useEffect(() => {
axios.get("/api/post/posts", config).then((response) => {
setPosts(response.data);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
const handler = (item) => {
setPosts((oldPosts) => {
const findItem = oldPosts.find((post) => post._id === item._id);
if (findItem) {
return oldPosts.map((post) => (post._id === item._id ? item : post));
} else {
return [item, ...oldPosts];
}
});
};
socket.on("posts", handler);
return () => socket.off("posts", handler);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
/*
Toggle state of post boxes
*/
const toggleCommentBox = postId => {
if(openBoxes.includes(postId)){
setOpenBoxes(openBoxes.filter(x => x !== postId))
} else {
setOpenBoxes([...openBoxes, postId])
}
}
/*
Returns boolean if comment box is open on this post
*/
const isCommentBoxOpen = postId => openBoxes.includes(postId)
const postHandler = (e) => {
e.preventDefault();
dispatch(newPost(uname, upic, messages, media));
setMessages("");
};
const LikePost = (postId) => {
likePosts(postId, user._id, user.name, user.pic);
};
const UnlikePost = (postId) => {
unlikePosts(postId);
};
const submitComment = (postId) => {
dispatch(newPostComment(postId, uname, upic, comment));
setComment("");
};
const LikeCommentPost = (postId, commentId) => {
dispatch(likePostComment(postId, commentId, user._id, user.name, user.pic));
};
const UnlikeCommentPost = (postId, commentId) => {
dispatch(unlikePostComment(postId, commentId));
};
return error ? (
<span>{error}</span>
) : (
<div className="dashboard">
<DashboardHeader/>
<div className="dashboard__container">
<div className="dashboard__sidebar">
<DashboardSidebar/>
</div>
<div className="dashboard__content">
<div className="dashboard__contentLeft">
<div className="dashboard__messenger">
<div className="dashboard__messengerTop">
<Avatar src={user.pic} className="dashboard__messengerAvatar"/>
<input
type="text"
placeholder={`What's on your mind, ${user.name}`}
value={messages}
onChange={(e) => setMessages(e.target.value)}
/>
<SendIcon
className="dashboard__messengerPostButton"
onClick={postHandler}
/>
</div>
<div className="dashboard__messengerBottom">
<ImageSearchIcon
className="dashboard__messengerImageIcon"
value={media}
onChange={(e) => setMedia((e) => e.target.value)}
/>
<VideoLibraryIcon className="dashboard__messengerVideoIcon"/>
</div>
</div>
<div className="dashboard__postsContainer">
<FlipMove>
{posts.map((post, i) => (
<div className="dashboard__post" key={i}>
<MoreHorizIcon className="dashboard__postOptions"/>
<div className="dashboard__postTop">
<Avatar
className="dashboard__postUserPic"
src={post.upic}
/>
<h3>{post.uname}</h3>
</div>
<div className="dashboard__postBottom">
<p>{post.message}</p>
{media === null ? (
""
) : (
<div className="dashboard__postMedia">{media}</div>
)}
</div>
<div className="dashboard__postActions">
{isCommentBoxOpen(post.id) ? (
<ChatBubbleOutlineIcon
onClick={() => toggleCommentBox(post._id)}
className="dashboard__actionComment"
/>
) : (
<ChatBubbleOutlineIcon
onClick={() => toggleCommentBox(post._id)}
className="dashboard__actionComment"
/>
)}
<label
id="totalLikes"
className="dashboard__comments"
style={{color: "forestgreen"}}
>
{post.commentCount}
</label>
{post.likes.find((like) => like.uid === user._id) ? (
<FavoriteIcon
onClick={() => UnlikePost(post._id)}
className="dashboard__actionUnlike"
/>
) : (
<FavoriteBorderIcon
onClick={() => LikePost(post._id)}
className="dashboard__actionLike"
/>
)}
<label
id="totalLikes"
className="dashboard__likes"
style={{color: "forestgreen"}}
>
{post.likeCount}
</label>
</div>
<div
className={
toggle
? "dashboard__commentContent toggle"
: "dashboard__commentContent"
}
>
<div className="dashboard__postComments">
{post.comments.map((comment) => (
<div
key={comment.toString()}
className="dashboard__postComment"
>
<div className="dashboard__postCommentTop">
<Avatar src={comment.upic}/>
<h4>{comment.uname}</h4>
</div>
<p>{comment.message}</p>
<div className="dashboard__postCommentActions">
{comment.likes.find(
(like) => like.uid === user._id
) ? (
<FavoriteIcon
onClick={() =>
UnlikeCommentPost(post._id, comment._id)
}
className="dashboard__actionUnlike"
/>
) : (
<FavoriteBorderIcon
onClick={() =>
LikeCommentPost(post._id, comment._id)
}
className="dashboard__actionLike"
/>
)}
<label
id="totalLikes"
className="dashboard__likes"
style={{color: "forestgreen"}}
>
{comment.likeCount}
</label>
</div>
</div>
))}
</div>
<div className="dashboard__commentInput">
<input
type="text"
placeholder="Comment post"
value={comment}
onChange={(e) => setComment(e.target.value)}
/>
<button onClick={() => submitComment(post._id)}>
Send
</button>
</div>
</div>
</div>
))}
</FlipMove>
</div>
</div>
<div className="dashboardContentRight"></div>
</div>
</div>
</div>
);
}
Let me know if it works or you need more explanation
Hi i am working on a React application where there are four inputs.when a user add an input the element will be added to the wrapper.In the following code add operation works fine but remove operation is not working properly ,it is not removing the corresponding element.Another problem the values on the inputs fields not present when the component re-renders.so experts guide me how i can achieve removing the corresponding row when the remove button is clicked and the input values should not be reset when the component re-renders
So when I refresh the page and click to remove an input it will clear all other input data. How can I fix this problem ?
Update added full component to question:
const Agreement = (props) => {
const { agreement, editable, teamData, teamId, fetchTeamData } = props;
const [editing, setEditing] = useState(false);
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const [showErrors, setShowErrors] = useState(false);
const [errorsArr, setErrorsArr] = useState();
const initialFormState = {
rule_0: teamData.rule_0,
rule_1: teamData.rule_1,
rule_2: teamData.rule_2,
rule_3: teamData.rule_3,
creator: teamData.User.public_user_id,
};
const [updateTeamData, setUpdateTeamData] = useState(initialFormState);
const [inputs, setInputs] = useState(teamData.rules);
const handleChange = (event) => {
const { name, value } = event.target;
// Update state
setUpdateTeamData((prevState) => ({
...prevState,
[name]: value,
}));
};
// Add more input
const addInputs = () => {
setInputs([...inputs, { name: `rule-${inputs.length + 1}` }]);
};
// handle click event of the Remove button
const handleRemoveClick = (index) => {
const list = [...inputs];
list.splice(index, 1);
setInputs(list);
};
const clearInput = (dataName) => {
setUpdateTeamData((prevState) => {
delete prevState[dataName];
return {
...prevState,
};
});
};
const handleSubmit = async (event) => {
event.preventDefault();
setEditing(false);
// Send update request
console.log(updateTeamData);
const res = await axios.put(`/api/v1/teams/team/${teamId}`, updateTeamData);
// If no validation errors were found
// Validation errors don't throw errors, it returns an array to display.
if (res.data.validationErrors === undefined) {
// Clear any errors
setErrorsArr([]);
// Hide the errors component
setShowErrors(false);
// Call update profiles on parent
fetchTeamData();
} else {
// Set errors
setErrorsArr(res.data.validationErrors.errors);
// Show the errors component
setShowErrors(true);
}
};
const handleCancel = () => {
setEditing(false);
};
useEffect(() => {
if (agreement === "default") {
setTitle(defaultTitle);
setInputs(teamData.rules);
} else {
setTitle(agreement.title ?? "");
}
}, [agreement, teamData]);
return (
<div className="team-agreement-container">
{!editing && (
<>
<h4 className="team-agreement-rules-title">{title}</h4>
{editable && (
<div className="team-agreement-rules">
<EditOutlined
className="team-agreement-rules-edit-icon"
onClick={() => setEditing(true)}
/>
</div>
)}
<p className="team-agreement-rules-description">{description}</p>
{teamData.rules.map((rule, index) => (
<div className="team-agreement-rule-item" key={`rule-${index}`}>
{rule ? (
<div>
<h4 className="team-agreement-rule-item-title">
{`Rule #${index + 1}`}
</h4>
<p className="team-agreement-rule-item-description">
- {rule}
</p>
</div>
) : (
""
)}
</div>
))}
</>
)}
{/* Edit rules form */}
{editing && (
<div className="team-agreement-form">
{showErrors && <ModalErrorHandler errorsArr={errorsArr} />}
<h1>Rules</h1>
{inputs.map((data, idx) => {
return (
<div className="agreement-form-grid" key={`${data}-${idx}`}>
<button
type="button"
className="agreement-remove-button"
onClick={() => {
handleRemoveClick(idx);
clearInput(`rule_${idx}`);
}}
>
<Remove />
</button>
<input
type="text"
placeholder={`Rule ${idx + 1}`}
defaultValue={teamData.rules[idx]}
name={`rule_${idx}`}
onChange={handleChange}
/>
</div>
);
})}
{inputs.length < 4 && (
<div className="team-agreement-add-rule">
<button type="submit" onClick={addInputs}>
<Add />
</button>
</div>
)}
<div className="div-button">
<button className="save-button" onClick={handleSubmit}>
Save
</button>
<button className="cancel-button" onClick={handleCancel}>
Cancel
</button>
</div>
</div>
)}
</div>
);
};
export default Agreement;
Hi i am working on a React application where there are four options.when a user select an option corresponding input element will be added to the wrapper.In the following code add operation works fine but remove operation is not working properly ,it is not removing the corresponding element.Another problem the values on the inputs fields not present when the component re-renders.so experts guide me how i can acheive removing the corresponding row when the remove button is clicked and the input values should not be reset when the component re-renders.
But when I submit the input it will appear my data perfectly and when i restart the page and just click into edit and hit submit with the defaultValue it just clear all the data and send back to my backend with undefined value like this: [ undefined, undefined, undefined, undefined ]
Here is my full component:
const Agreement = (props) => {
const { agreement, editable, teamData, teamId, fetchTeamData } = props;
const [editing, setEditing] = useState(false);
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const [showErrors, setShowErrors] = useState(false);
const [errorsArr, setErrorsArr] = useState();
const initialFormState = {
rule_0: teamData.rules.rule_0,
rule_1: teamData.rules.rule_1,
rule_2: teamData.rules.rule_2,
rule_3: teamData.rules.rule_3,
creator: teamData.User.public_user_id,
};
const [updateTeamData, setUpdateTeamData] = useState(initialFormState);
const [inputs, setInputs] = useState(teamData.rules);
const handleChange = (event) => {
const { name, value } = event.target;
// Update state
setUpdateTeamData((prevState) => ({
...prevState,
[name]: value,
}));
};
// Add more input
const addInputs = () => {
setInputs([...inputs, { name: `rule_${inputs.length + 1}` }]);
};
// handle click event of the Remove button
const removeInputs = (index) => {
const list = [...inputs];
list.splice(index, 1);
setInputs(list);
};
const clearInput = (dataName) => {
setUpdateTeamData((prevState) => {
delete prevState[dataName];
return {
...prevState,
};
});
};
const handleSubmit = async (event) => {
event.preventDefault();
setEditing(false);
// Send update request
const res = await axios.put(`/api/v1/teams/team/${teamId}`, updateTeamData);
// If no validation errors were found
// Validation errors don't throw errors, it returns an array to display.
if (res.data.validationErrors === undefined) {
// Clear any errors
setErrorsArr([]);
// Hide the errors component
setShowErrors(false);
// Call update profiles on parent
fetchTeamData();
} else {
// Set errors
setErrorsArr(res.data.validationErrors.errors);
// Show the errors component
setShowErrors(true);
}
};
const handleCancel = () => {
setEditing(false);
};
useEffect(() => {
if (agreement === "default") {
setTitle(defaultTitle);
setInputs(teamData.rules);
} else {
setTitle(agreement.title ?? "");
}
}, [agreement, teamData]);
console.log("teamData.rules", teamData.rules);
console.log("inputs", inputs);
return (
<div className="team-agreement-container">
{!editing && (
<>
<h4 className="team-agreement-rules-title">{title}</h4>
{editable && (
<div className="team-agreement-rules">
<EditOutlined
className="team-agreement-rules-edit-icon"
onClick={() => setEditing(true)}
/>
</div>
)}
<p className="team-agreement-rules-description">{description}</p>
{teamData.rules.map((rule, index) => (
<div className="team-agreement-rule-item" key={`rule-${index}`}>
{rule ? (
<div>
<h4 className="team-agreement-rule-item-title">
{`Rule #${index + 1}`}
</h4>
<p className="team-agreement-rule-item-description">
- {rule}
</p>
</div>
) : (
""
)}
</div>
))}
</>
)}
{/* Edit rules form */}
{editing && (
<div className="team-agreement-form">
{showErrors && <ModalErrorHandler errorsArr={errorsArr} />}
<h1>Rules</h1>
{inputs.map((data, idx) => {
return (
<div className="agreement-form-grid" key={`${data}-${idx}`}>
<button
type="button"
className="agreement-remove-button"
onClick={() => {
removeInputs(idx);
clearInput(`rule_${idx}`);
}}
>
<Remove />
</button>
<input
name={`rule_${idx}`}
onChange={handleChange}
value={teamData.rules[idx]}
/>
</div>
);
})}
{inputs.length < 4 && (
<div className="team-agreement-add-rule">
<button type="submit" onClick={addInputs}>
<Add />
</button>
</div>
)}
<div className="div-button">
<button className="save-button" onClick={handleSubmit}>
Save
</button>
<button className="cancel-button" onClick={handleCancel}>
Cancel
</button>
</div>
</div>
)}
</div>
);
};
export default Agreement;
How can I fix this error?
My thought is the problem is around [inputs, setInputs]
Try this
<input
//..
onChange={(event) => handleChange(event.target.value)}
//..
/>
then in your "handleChange" function
const handleChange = (event) => {
const { name, value } = event;
//....
};
I fire an onclick called onEditScheduleClick this sets an object which is all fine, however when I click on an edit button on a different row data is overwritten in the props.setScheduleFieldData.
when I click the editButton the 70 will display too 70 and it will have the same dataabase name as ID 156.
As you can see, when I hit the edit button the data is logged fine. I am thinking I may need to create a unique instance for each row? Could someone help me out please!
Schedules
const schedules = () => {
const autoComplete = useContext(AutoCompleteContext)
const snackbar = useContext(SnackbarContext)
const loading = useContext(LoadingContext)
const user = useContext(UserContext)
const [scheduleData, setScheduleData] = useState([])
const [showScheduleModal, setShowScheduleModal] = useState(false)
const [scheduleFieldData, setScheduleFieldData] = useState({})
const [database, setDatabase] = useState('')
useEffect(() => {
async function onLoadScheduleData(){
loading.setLoading(true)
const [results, projectResults,databaseAutoCompleteResults] = await Promise.all([
get('get_testing_schedules', user.user),
get('get_projects_autocomplete/b', user.user),
get('get_databases_autocomplete/a', user.user)
])
autoComplete.setProjectsAutoComplete(projectResults)
autoComplete.setDatabasesAutoComplete(databaseAutoCompleteResults)
setScheduleData(results.data)
loading.setLoading(false)
}
onLoadScheduleData()
}, [])
const sortedScheduleData = [].concat(scheduleData)
.sort((a, b) => a.id < b.id ? 1 : -1)
const showModal = () => {
setShowScheduleModal(true)
setScheduleFieldData({})
}
const onCloseScheduleModal = () => setShowScheduleModal(false)
const onScheduleFieldUpdate = (e, valueFromAutoComplete, nameFromAutoComplete) => {
const name = nameFromAutoComplete ? nameFromAutoComplete
: e.target.name || e.target.getAttribute('name')
const value = valueFromAutoComplete ? valueFromAutoComplete.map(val => val.value).join(',')
: e.target.innerText ? e.target.innerText
: e.target.value
setScheduleFieldData({...scheduleFieldData, ...{[name]: value}})
console.log(name)
}
const onDatabaseSelect = async (e, value) => {
autoComplete.onDatabaseAutoCompleteFieldUpdate(e)
const name = e.target.getAttribute('name')
console.log(name)
setDatabase(value)
setScheduleFieldData({...scheduleFieldData, ...{[name]: value}})
}
console.log(scheduleFieldData, 'scheduleFieldData');
const onSubmitTestingScheduleClick = async () => {
loading.setLoading(true)
const results = await verifiedPost('post_testing_schedule', scheduleFieldData, user.user)
if (results.status === 0) {
setShowScheduleModal(false)
setScheduleFieldData({})
setScheduleData(results.data)
} else if(results.status >= 20 && results.status <= 30) {
user.setSessionTokenMatches(false)
}
snackbar.statusCheck(results)
loading.setLoading(false)
}
return (!scheduleData ? null :
<PageWrapper title='Schedules'>
<div style={{marginBottom: '20px'}}>
<Button
onClick={showModal}
className='Button Dark Main'
text='CREATE SCHEDULE'
/>
</div>
<div className='Card'>
<div className='TableTopbar ScheduleGrid'>
<div>ID</div>
<div>Interval</div>
<div>Project ID</div>
<div>Database</div>
<div>Create Timestamp</div>
<div>Create User Id</div>
<div>Edit</div>
</div>
{sortedScheduleData.map(schedule =>
<ScheduleDataRow
key={schedule.id}
schedule={schedule}
setScheduleData={setScheduleData}
onScheduleFieldUpdate={onScheduleFieldUpdate}
setScheduleFieldData={setScheduleFieldData}
scheduleFieldData={scheduleFieldData}
onDatabaseSelect={onDatabaseSelect}
/>
)}
</div>
<ScheduleModal
showScheduleModal={showScheduleModal}
onCloseScheduleModal={onCloseScheduleModal}
onScheduleFieldUpdate={onScheduleFieldUpdate}
scheduleFieldData={scheduleFieldData}
onSubmitTestingScheduleClick={onSubmitTestingScheduleClick}
scheduleFieldData={scheduleFieldData}
// onFieldUpdate={onFieldUpdate}
onDatabaseSelect={onDatabaseSelect}
database={database}
/>
</PageWrapper>
)
}
export default schedules
ScheduleDataRow:
const scheduleDataRow = props => {
const testing = useContext(TestingFrameworkContext)
const autoComplete = useContext(AutoCompleteContext)
const snackbar = useContext(SnackbarContext)
const loading = useContext(LoadingContext)
const user = useContext(UserContext)
const [usageMode, setUsageMode] = useState('Read')
const onEditScheduleClick = () => {
props.setScheduleFieldData({
'id': props.schedule.id,
'interval': props.schedule.interval,
'project_id': props.schedule.project_id,
'database': props.schedule.database.toString(),
})
setUsageMode('Edit')
}
const onSubmitTestingScheduleClick = async () => {
loading.setLoading(true)
const results = await verifiedPost('post_testing_schedule', props.scheduleFieldData, user.user)
if (results.status === 0) {
props.setScheduleData(results.data)
setUsageMode('Read')
} else if(results.status >= 20 && results.status <= 30) {
user.setSessionTokenMatches(false)
}
snackbar.statusCheck(results)
loading.setLoading(false)
}
const onCancelEditScheduleClick = () => setUsageMode('Read')
const getPartialDatabase = (database) => {
const split = database.split('::')
return split[split.length - 1]
}
return (
<div className='Table ScheduleGrid'>
{usageMode === 'Read' ?
<React.Fragment>
<div>{props.schedule.id}</div>
<div>{props.schedule.interval}</div>
<div>{props.schedule.project_id}</div>
<div>{getPartialDatabase(props.schedule.database)}</div>
<div>{props.schedule.create_timestamp}</div>
<div>{props.schedule.create_user_id}</div>
<div>
<EditIcon
style={{padding: '2px', width: '0.8em', height: '0.8em', marginRight: '5px'}}
className='CircleButton'
onClick={onEditScheduleClick}
/>
</div>
</React.Fragment>
:
<React.Fragment>
<div>{props.schedule.id}</div>
{configs.map((config, k) => {
const Field = config.field
return (
<div key={config.name} className='Main'>
<Field
uniqueIdentifier={config.name}
placeholder={config.label}
name={config.name}
uniqueIdentifier={k}
onChange={props.onScheduleFieldUpdate}
onSelect={props.onScheduleFieldUpdate}
onAutoCompleteOnChange={autoComplete.onProjectAutoCompleteFieldUpdate}
options={config.name === 'project_id' ? autoComplete.projectsAutoComplete : config.name === 'database' ? autoComplete.databasesAutoComplete : config.options}
value={props.scheduleFieldData[config.name]}
initialValues={
props.scheduleFieldData[config.name].split(',').filter(x => x).map(x => ({label: x, value: x}))
}
/>
</div>
)
})}
<div><AutoCompleteSingle
name='database'
label='Database'
options={autoComplete.databasesAutoComplete}
onChange={autoComplete.onDatabaseAutoCompleteFieldUpdate}
onSelect={props.onDatabaseSelect}
initialValue={props.scheduleFieldData['database']}
uniqueIdentifier={1}
triggerOnSelectOnBackspace
/></div>
<div>{props.schedule.create_timestamp}</div>
<div>{props.schedule.create_user_id}</div>
<div style={{display: 'flex', alignItems: 'flex-start'}}>
<div>
<Button
style={{marginRight: '10px'}}
text='Submit'
disabled={false}
className='Button Dark Main'
onClick={onSubmitTestingScheduleClick}
/>
</div>
<div>
<Button
text='Cancel'
className='Button Dark Main'
onClick={onCancelEditScheduleClick}
/>
</div>
</div>
</React.Fragment>
}
</div>
)
}
scheduleDataRow.propTypes = {
schedule: PropTypes.object,
setScheduleData: PropTypes.func
}
export default scheduleDataRow