I'm trying to use useEffect to set the behaviour for my room and at first, i wanna use the token and roomName to connect to the Twilio Video service. However, i got the error as "token must be a string" in my Room.js. I don't know what's wrong? can anyone help me?
VideoChat.js:
import React, { useState, useCallback } from "react";
import Lobby from "./Lobby";
import Room from "./Room"
function VideoChat() {
const [username, setUsername] = useState("");
const [roomName, setRoomName] = useState("");
const [token, setToken] = useState(null);
const handleUsernameChange = useCallback((event) => {
setUsername(event.target.value);
}, []);
const handleRoomNameChange = useCallback((event) => {
setRoomName(event.target.value);
}, []);
const handleSubmit = useCallback(
async (event) => {
event.preventDefault();
const data = await fetch("/video/token", {
method: "POST",
body: JSON.stringify({
identity: username,
room: roomName,
}),
headers: {
"Content-Type": "application/json",
},
}).then((res) => res.json());
setToken(data.token);
},
[username, roomName]
);
const handleLogout = useCallback((event) => {
setToken(null);
}, []);
return (
<div>
if (token){" "}
{
<div>
<Room roomName={roomName} token={token} handleLogout={handleLogout} />
</div>
}{" "}
else{" "}
{
<Lobby
username={username}
roomName={roomName}
handleUsernameChange={handleUsernameChange}
handleRoomNameChange={handleRoomNameChange}
handleSubmit={handleSubmit}
/>
}{" "}
</div>
);
}
export default VideoChat;
Lobby.js:
import React from "react";
const Lobby = ({
username,
handleUsernameChange,
roomName,
handleRoomNameChange,
handleSubmit
}) => {
return (
<form onSubmit={handleSubmit}>
<h2> Enter a room </h2>{" "}
<div>
<label htmlFor="name"> Name: </label>{" "}
<input
type="text"
id="field"
value={username}
onChange={handleUsernameChange}
required
/>
</div>{" "}
<div>
<label htmlFor="room"> Room name: </label>{" "}
<input
type="text"
id="room"
value={roomName}
onChange={handleRoomNameChange}
required
/>
</div>{" "}
<button type="submit"> Submit </button>{" "}
</form>
);
};
export default Lobby;
Room.js:
import React, { useState, useEffect } from "react";
import Video from "twilio-video";
const Room = ({ roomName, token, handleLogout }) => {
const [room, setRoom] = useState(null);
const [participants, setParticipants] = useState([]);
const remoteParticipants = participants.map(participant=> {
<p key={participant.sid}>{participant.identity}</p>
})
useEffect(() => {
const participantConnected = participant => {
setParticipants(prevParticipants => [...prevParticipants, participant]);
};
const participantDisconnected = participant => {
setParticipants(prevParticipants =>
prevParticipants.filter(p => p !== participant)
);
};
Video.connect(token, {
name: roomName
}).then(room => {
setRoom(room);
room.on('participantConnected', participantConnected);
room.on('participantDisconnected', participantDisconnected);
room.participants.forEach(participantConnected);
});
});
return (
<div className="room">
<h2>Room: {roomName}</h2>
<button onClick={handleLogout}>Log out</button>
<div className="local-participant">
{room ? (<p key={room.localParticipant.sid}>{room.localParticipant.identity}</p>) : ''}
</div>
<h3>Remote Participants</h3>
<p>{remoteParticipants}</p>
</div>
)
};
export default Room;
Sandbox link: https://codesandbox.io/s/beautiful-bhaskara-oo12s?file=/src/Lobby.js
Related
I wanna make a function that, When i submit the form i want to send the username and room name to the server to exchange for an access token, and if there's a token, my Room component will be rendered and you can see the roomName being rendered. However when i click the submit button, nothing happened. What's wrong with my codes?
VideoChat.js:
import React, { useState, useCallback } from "react";
import Lobby from "./Lobby";
import Room from "./Room"
function VideoChat() {
const [username, setUsername] = useState("");
const [roomName, setRoomName] = useState("");
const [token, setToken] = useState(null);
const handleUsernameChange = useCallback((event) => {
setUsername(event.target.value);
}, []);
const handleRoomNameChange = useCallback((event) => {
setRoomName(event.target.value);
}, []);
const handleSubmit = useCallback(
async event => {
event.preventDefault();
const data = await fetch("/video/token", {
method: "POST",
body: JSON.stringify({
identity: username,
room: roomName,
}),
headers: {
"Content-Type": "application/json",
},
}).then((res) => res.json());
setToken(data.token);
},
[username, roomName]
);
const handleLogout = useCallback((event) => {
setToken(null);
}, []);
Lobby.js:
import React from "react";
const Lobby = ({
username,
handleUsernameChange,
roomName,
handleRoomNameChange,
handleSubmit
}) => {
return (
<form onSubmit={handleSubmit}>
<h2> Enter a room </h2>{" "}
<div>
<label htmlFor="name"> Name: </label>{" "}
<input
type="text"
id="field"
value={username}
onChange={handleUsernameChange}
required
/>
</div>{" "}
<div>
<label htmlFor="room"> Room name: </label>{" "}
<input
type="text"
id="room"
value={roomName}
onChange={handleRoomNameChange}
required
/>
</div>{" "}
<button type="submit" > Submit </button>{" "}
</form>
);
};
export default Lobby;
return (
<div>
{token ? (<div><Room roomName={roomName} token={token} handleLogout={handleLogout} /></div>)
: (<div>
<Lobby
username={username}
roomName={roomName}
handleUsernameChange={handleUsernameChange}
handleRoomNameChange={handleRoomNameChange}
handleSubmit={handleSubmit}
/>
</div>)
}
</div>
);
}
export default VideoChat;
Room.js:
import React, { useState, useEffect } from "react";
import Video from "twilio-video";
const Room = ({ roomName, token, handleLogout }) => {
const [room, setRoom] = useState(null);
const [participants, setParticipants] = useState([]);
const remoteParticipants = participants.map(participant=> {
<p key={participant.sid}>{participant.identity}</p>
})
useEffect(() => {
const participantConnected = participant => {
setParticipants(prevParticipants => [...prevParticipants, participant]);
};
const participantDisconnected = participant => {
setParticipants(prevParticipants =>
prevParticipants.filter(p => p !== participant)
);
};
Video.connect(token, {
name: roomName
}).then(room => {
setRoom(room);
room.on('participantConnected', participantConnected);
room.on('participantDisconnected', participantDisconnected);
room.participants.forEach(participantConnected);
});
});
return (
<div className="room">
<h2>Room: {roomName}</h2>
<button onClick={handleLogout}>Log out</button>
<div className="local-participant">
{room ? (<p key={room.localParticipant.sid}>{room.localParticipant.identity}</p>) : ''}
</div>
<h3>Remote Participants</h3>
<p>{remoteParticipants}</p>
</div>
)
};
export default Room;
Sandbox link: https://codesandbox.io/s/video-chat-y67nv?file=/src/Lobby.js
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
I want to create an edit screen. I have a component called Task that looks like this
const Task = ({task}) => {
return (
<li>
<div>
<div>{task.text}</div>
{task.day}
</div>
<div className="icons">
<Link
to={`/edit/${task.id}`} >
<RiEdit2FillIcon />
</Link>
</div>
</li>
)
}
That goes to a parent component with a tasks.map() and then to the main component that will render the list of tasks. But from this component, I want to click on that Edit Icon and open an Edit screen that is already Routed like this <Route path='/edit/:id' component={EditTask}/> That EditTask component is what I am working on now
import React from 'react'
import {useState, useEffect} from 'react'
import { Link } from 'react-router-dom'
import Task from './components/Task'
const EditTask = () => {
const api ="http://localhost:5000"
const [tasks, setTasks] = useState([])
const [task, setTask] = useState([])
const [text, setText] = useState('')
const [day, setDay] = useState('')
const [reminder, setReminder] = useState(false)
const onSubmit = (e) => {
e.preventDefault()
updateData()
}
//Get Request
useEffect(() => {
const getTask = async () => {
const tasksFromServer = await fetchTask()
setTasks(tasksFromServer)
}
getTask()
},[])
const fetchTask = async (id) => {
const res = await fetch(`${api}/tasks/${id}`)
const data = await res.json()
console.log(data)
return data
}
//Update request
const updateData = async (id) => {
const taskToEdit = await fetchTask(id)
const updateTask = {
...taskToEdit,
reminder: !taskToEdit.reminder,
text: taskToEdit.text,
day: taskToEdit.day
}
const res = await fetch(`${api}/tasks/${id}`, {
method: 'PUT',
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify(updateTask)
})
const data = await res.json()
setTasks(
tasks.map((task) =>
task.id === id
? {
...task,
reminder: data.reminder,
text: data.text,
day: data.day
}
: task
)
)
}
return (
<div>
<header className='header'>
<h1>Edit</h1>
<Link to="/" className="btn btn-primary">Go Back</Link>
</header>
<form className="add-form" onSubmit={onSubmit}>
<Task task={task}/>
<div className="form-control">
<label>Task</label>
<input type="text" placeholder="Add Task" value={text} onChange={(e)=> setText(e.target.value)} />
</div>
<div className="form-control">
<label>Day & Time</label>
<input type="text" placeholder="Add Day & Time" value={day} onChange={(e)=> setDay(e.target.value)}/>
</div>
<div className="form-control form-control-check">
<label>Set Reminder</label>
<input type="checkbox" checked={reminder} value={reminder} onChange={(e)=> setReminder(e.currentTarget.checked)}/>
</div>
<input className="btn btn-block" type="submit" value="Save Task" />
</form>
</div>
)
}
export default EditTask
I'm a bit lost here. I can't figure out how to pass the ID from Task.js to EditTask.js and populate the form with the data form that ID.
Thanks in advance
You can get id in EditTask with useParams in "react-router
import { useParams } from "react-router";
const EditTask = () => {
const { id } = useParams();
}
I am trying to add the objects into the localstorage by the use of state but not able to get the desired results as i want to save the username,password,firstname,last name into the localstorage and also display these elements of object into the HomePage and getting error 'TypeError: newData is not iterable'....Any help will be much appreciated.
App.js
import LoginForm from "./Component/LoginForm/LoginForm";
import HomePage from "./Component/HomePage/HomePage";
import { useState, useEffect } from "react";
const user = [];
function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [userData,setUserData] = useState(user);
console.log('userData: ',userData);
localStorage.setItem('CompleteData',JSON.stringify(userData));
const getObj = JSON.parse(localStorage.getItem('CompleteData'));
console.log('getObj: ',getObj);
useEffect(() => {
const isUserLoggedIn = localStorage.getItem("isLoggedInn");
if (isUserLoggedIn === "1") {
setIsLoggedIn(true);
}
}, []);
const loginHandler = (username,first,last,pass) => {
localStorage.setItem("isLoggedInn", "1");
setIsLoggedIn(true);
localStorage.setItem('currentUsername',username);
localStorage.setItem('currentFirstname',first);
localStorage.setItem('currentLastname',last);
localStorage.setItem('currentPassword',pass);
};
const logoutHandler = () => {
localStorage.removeItem("isLoggedInn");
setIsLoggedIn(false);
};
const onSaveDataHandler = (newData) => {
setUserData((prevData)=>{
return [prevData,...newData];
});
};
const dataFormHandler = (username, first,last,pass) => {
return [
localStorage.getItem('currentUsername'),
localStorage.getItem('currentFirstname'),
localStorage.getItem('currentLastname'),
localStorage.getItem('currentPassword')
];
}
return (
<>
<div>
{!isLoggedIn && (
<LoginForm
onLogin={loginHandler}
onSaveData={onSaveDataHandler}
usersD={getObj}
/>
)}
{isLoggedIn && (
<HomePage onLogout={logoutHandler} user={dataFormHandler()} />
)}
</div>
</>
);
}
export default App;
import styles from "./LoginForm.module.css";
import { useState } from "react";
import SignUp from "../SignUp/SignUp";
const LoginForm = (props) => {
const [enteredUsername, setEnteredUsername] = useState("");
const [enteredPassword, setEnteredPassword] = useState("");
const [isTrue, setTrue] = useState(true);
const [isClicked, setIsClicked] = useState(false);
const onChangeHandlerUsername = (event) => {
setEnteredUsername(event.target.value);
if (event.target.value === enteredUsername) {
setTrue(true);
}
};
const onChangeHandlerPassword = (event) => {
setEnteredPassword(event.target.value);
if (event.target.value === enteredPassword) {
setTrue(true);
}
};
const onSubmitHandler = (event) => {
event.preventDefault();
props.usersD.forEach((element,i)=>{
if (
enteredUsername === element[i].username &&
enteredPassword === element[i].password
){
props.onLogin(element[i].username,element[i].firstname,element[i].lastname,element[i].password);
}
else {
setTrue(false);
};
})
};
const onClickHandler = () => {
setIsClicked(true);
};
const sendDataToChild = (entereduserData) =>{
const userData = {
id: Math.random().toString(),
...entereduserData
};
props.onSaveData(userData);
}
return (
<>
{isClicked &&
enteredUsername !== Object.keys(props.usersD).username &&
enteredPassword !== Object.keys(props.usersD).password ? (
<SignUp dataTransfer={sendDataToChild} clk={setIsClicked}/>
) : (
<form onSubmit={onSubmitHandler}>
<h1 className={styles.blink_me}>W E L C O M E</h1>
<div className={`${styles.box} ${!isTrue && styles.wrong}`}>
<h1>Login</h1>
<input
type="text"
value={enteredUsername}
placeholder="Enter Username"
className={styles.email}
onChange={onChangeHandlerUsername}
></input>
<input
type="password"
value={enteredPassword}
placeholder="Enter Password"
className={styles.email}
onChange={onChangeHandlerPassword}
></input>
<div>
<button className={styles.btn}>Sign In</button>
</div>
<div>
<button
onClick={onClickHandler}
type="button"
className={styles.btn2}
>
Sign Up
</button>
</div>
<div>
Forget Password
</div>
</div>
</form>
)}
</>
);
};
export default LoginForm;
HomePage.js
import styler from './HomePage.module.css';
const HomePage = (props) =>{
const name = props.user[0];
const first = props.user[1];
const last = props.user[2];
const pass = props.user[3];
return(
<>
<div className={styler.body}>
<h2>Login Successfull !</h2>
{/* {props.user.map(users=><p username={users[0]} password={users[1]}/>)} */}
<p>Name: {name}</p>
<p>FirstName: {first}</p>
<p>LastName: {last}</p>
<p>Password: {pass}</p>
<button type='submit' onClick={props.onLogout} className={styler.button}>Logout</button>
</div>
</>
);
}
export default HomePage;
It's my first time doing testing in general and I am trying to test a register page (check if the forms input are correctly working and if will create the user at the end). This is my register page:
import React, { useState, useRef } from "react";
import Form from "react-validation/build/form";
import Input from "react-validation/build/input";
import CheckButton from "react-validation/build/button";
import { isEmail } from "validator";
import AuthService from "../services/auth.service";
const required = (value) => {
if (!value) {
return (
<div className="alert alert-danger" role="alert">
This field is required!
</div>
);
}
};
const validEmail = (value) => {
if (!isEmail(value)) {
return (
<div className="alert alert-danger" role="alert">
This is not a valid email.
</div>
);
}
};
const vusername = (value) => {
if (value.length < 3 || value.length > 20) {
return (
<div className="alert alert-danger" role="alert">
The username must be between 3 and 20 characters.
</div>
);
}
};
const vpassword = (value) => {
if (value.length < 6 || value.length > 40) {
return (
<div className="alert alert-danger" role="alert">
The password must be between 6 and 40 characters.
</div>
);
}
};
const Register = (props) => {
const form = useRef();
const checkBtn = useRef();
const [username, setUsername] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [successful, setSuccessful] = useState(false);
const [message, setMessage] = useState("");
const onChangeUsername = (e) => {
const username = e.target.value;
setUsername(username);
};
const onChangeEmail = (e) => {
const email = e.target.value;
setEmail(email);
};
const onChangePassword = (e) => {
const password = e.target.value;
setPassword(password);
};
const handleRegister = (e) => {
e.preventDefault();
setMessage("");
setSuccessful(false);
form.current.validateAll();
if (checkBtn.current.context._errors.length === 0) {
AuthService.register(username, email, password).then(
(response) => {
setMessage(response.data.message);
setSuccessful(true);
},
(error) => {
const resMessage =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
setMessage(resMessage);
setSuccessful(false);
}
);
}
};
return (
<div className="col-md-12">
<div className="card card-container">
<img
src="//ssl.gstatic.com/accounts/ui/avatar_2x.png"
alt="profile-img"
className="profile-img-card"
/>
<Form onSubmit={handleRegister} ref={form}>
{!successful && (
<div>
<div className="form-group">
<label htmlFor="username">Username</label>
<Input
type="text"
className="form-control"
name="username"
value={username}
onChange={onChangeUsername}
validations={[required, vusername]}
/>
</div>
<div className="form-group">
<label htmlFor="email">Email</label>
<Input
type="text"
className="form-control"
name="email"
value={email}
onChange={onChangeEmail}
validations={[required, validEmail]}
/>
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<Input
type="password"
className="form-control"
name="password"
value={password}
onChange={onChangePassword}
validations={[required, vpassword]}
/>
</div>
<div className="form-group">
<button className="btn btn-primary btn-block">Sign Up</button>
</div>
</div>
)}
{message && (
<div className="form-group">
<div
className={ successful ? "alert alert-success" : "alert alert-danger" }
role="alert"
>
{message}
</div>
</div>
)}
<CheckButton style={{ display: "none" }} ref={checkBtn} />
</Form>
</div>
</div>
);
};
export default Register;
My auhService is this :
import axios from "axios";
const API_URL = "http://localhost:8080/api/auth/";
const register = (username, email, password) => {
return axios.post(API_URL + "signup", {
username,
email,
password,
});
};
const login = (username, password) => {
return axios
.post(API_URL + "signin", {
username,
password,
})
.then((response) => {
if (response.data.accessToken) {
localStorage.setItem("user", JSON.stringify(response.data));
}
return response.data;
});
};
const logout = () => {
localStorage.removeItem("user");
};
const getCurrentUser = () => {
return JSON.parse(localStorage.getItem("user"));
};
export default {
register,
login,
logout,
getCurrentUser,
};
And this is my App.test.js :
import { configure, shallow, mount} from "enzyme";
import App from "./App"
import React, {useState} from "react";
import Adapter from "enzyme-adapter-react-16";
import Enzyme from "enzyme";
import Register from "./components/Register";
configure({ adapter: new Adapter() });
it("renders without crashing", () => {
shallow(<App />);
});
describe("<Register />", () => {
let wrapper;
const setState = jest.fn();
const useStateSpy = jest.spyOn(React, "useState")
useStateSpy.mockImplementation((init) => [init, setState]);
beforeEach(() => {
wrapper = Enzyme.mount(Enzyme.shallow(<Register />).get(0))
});
afterEach(() => {
jest.clearAllMocks();
});
describe("username input", () => {
it("Should capture title correctly onChange", () => {
const title = wrapper.find("input").at(0);
title.instance().value = "New user";
title.simulate("change");
expect(setState).toHaveBeenCalledWith("New user");
});
});
describe("email input", () => {
it("Should capture content correctly onChange", () => {
const content = wrapper.find("new Email").at(1);
content.instance().value = "Testing";
content.simulate("change");
expect(setState).toHaveBeenCalledWith("new Email");
});
})
});
By far I was trying the first two inputs(username and email), but I'm getting an error: "TypeError: Cannot read property 'child' of undefined". What shall I do here? I've been trying everyhting. Thank you guys!