Cannot get token - reactjs

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

Related

Whi i get the error "token must be a string"?

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

Show and hide selected div element via .map()

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

How do I retrieve an item data by id and pass it to another component in React16 with Hooks

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

After the login click i am not able to render the data that i got from Login form into the HomePage

After the login click i am not able to render the data that i got from Login form into the HomePage as i want to see the username and password into the HomePage after i entered the username and password that has already been in my local storage(at present this is not done as i am just checking the login with the dummy data).
App Component:
import LoginForm from "./Component/LoginForm/LoginForm";
import HomePage from "./Component/HomePage/HomePage";
import { useState, useEffect } from "react";
function App() {
const user = [{ username: "admin" }, { password: "admin" }];
const [isLoggedIn, setIsLoggedIn] = useState(false);
useEffect(() => {
const isUserLoggedIn = localStorage.getItem("isLoggedInn");
if (isUserLoggedIn === "1") {
setIsLoggedIn(true);
}
}, []);
const loginHandler = () => {
localStorage.setItem("isLoggedInn", "1");
setIsLoggedIn(true);
// return [username,password];
};
const logoutHandler = () => {
localStorage.removeItem("isLoggedInn");
setIsLoggedIn(false);
};
const onSaveDataHandler = (newData) =>{
console.log(newData);
console.log('inside app');
}
const dataFormHandler = (username,password) => {
return username;
}
return (
<div>
{!isLoggedIn && <LoginForm adminUser={user} onLogin={loginHandler} onSaveData={onSaveDataHandler} dataForm={dataFormHandler}/>}
{isLoggedIn && <HomePage onLogout={logoutHandler} user={dataFormHandler[0]}/>}
</div>
);
}
export default App;
LoginForm Component:
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();
if (
enteredUsername === props.adminUser[0].username &&
enteredPassword === props.adminUser[1].password
) {
props.onLogin();
props.dataForm(enteredUsername,enteredPassword);
} else {
setTrue(false);
}
};
const onClickHandler = () => {
setIsClicked(true);
};
const sendDataToChild = (entereduserData) =>{
const userData = {
...entereduserData,
id: Math.random().toString()
};
props.onSaveData(userData);
}
return (
<>
{isClicked &&
enteredUsername !== props.adminUser[0].username &&
enteredPassword !== props.adminUser[1].password ? (
<SignUp name={enteredUsername} dataTransfer={sendDataToChild}/>
) : (
<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 Component:
import styler from './HomePage.module.css';
const HomePage = (props) =>{
return(
<div className={styler.body}>
<h2>Login Successfull !</h2>
<p>Name: {props.user}</p>
<p>Password: {props.pass}</p>
<button type='submit' onClick={props.onLogout} className={styler.button}>Logout</button>
</div>
);
}
export default HomePage;
I found some points which I would like to highlight in the code.
dataFormHandler function: when this function is getting called from the LoginForm component, the output is not getting stored anywhere. See the below code snippet from the LoginForm component.
props.dataForm(enteredUsername,enteredPassword)
You need to store the username and password somewhere. For this you can directly utilize the onLogin() method of the LoginForm component and pass in the enteredUsername and enteredPassword as the arguments like below:
props.onLogin(enteredUsername, enteredPassword);
and then store them somewhere from the App component.
const loginHandler = (username, password) => {
localStorage.setItem("isLoggedInn", "1");
setIsLoggedIn(true);
localStorage.setItem("currentUsername", username);
localStorage.setItem("currentPassword", password);
// return [username,password];
};
Your dataFormHandler function returns just the username. That doesn't make sense to me. You can instead return an array of 2 elements, username and the password.
const dataFormHandler = () => {
return [
localStorage.getItem("currentUsername"),
localStorage.getItem("currentPassword")
];
};
Now to access these values in the HomePage component, you just need to access them as props.user[0] and props.user[1]
The code for all the three components can be found below: (ps- I have changed some file paths and removed the classNames and styles just to make it work on my machine)
App Component:
import LoginForm from "./components/LoginForm";
import HomePage from "./components/HomePage";
import { useState, useEffect } from "react";
function App() {
const user = [{ username: "admin" }, { password: "admin" }];
const [isLoggedIn, setIsLoggedIn] = useState(false);
useEffect(() => {
const isUserLoggedIn = localStorage.getItem("isLoggedInn");
if (isUserLoggedIn === "1") {
setIsLoggedIn(true);
}
}, []);
const loginHandler = (username, password) => {
localStorage.setItem("isLoggedInn", "1");
setIsLoggedIn(true);
localStorage.setItem("currentUsername", username);
localStorage.setItem("currentPassword", password);
// return [username,password];
};
const logoutHandler = () => {
localStorage.removeItem("isLoggedInn");
setIsLoggedIn(false);
};
const onSaveDataHandler = (newData) => {
console.log(newData);
console.log("inside app");
};
const dataFormHandler = () => {
return [
localStorage.getItem("currentUsername"),
localStorage.getItem("currentPassword")
];
};
return (
<div>
{!isLoggedIn && (
<LoginForm
adminUser={user}
onLogin={loginHandler}
onSaveData={onSaveDataHandler}
dataForm={dataFormHandler}
/>
)}
{isLoggedIn && (
<HomePage onLogout={logoutHandler} user={dataFormHandler()} />
)}
</div>
);
}
export default App;
LoginForm Component:
import { useState } from "react";
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);
}
console.log(enteredUsername);
};
const onChangeHandlerPassword = (event) => {
setEnteredPassword(event.target.value);
if (event.target.value === enteredPassword) {
setTrue(true);
}
console.log(enteredPassword);
};
const onSubmitHandler = (event) => {
event.preventDefault();
if (
enteredUsername === props.adminUser[0].username &&
enteredPassword === props.adminUser[1].password
) {
props.onLogin(enteredUsername, enteredPassword);
// props.dataForm(enteredUsername, enteredPassword);
} else {
setTrue(false);
}
};
const onClickHandler = () => {
setIsClicked(true);
};
const sendDataToChild = (entereduserData) => {
const userData = {
...entereduserData,
id: Math.random().toString()
};
props.onSaveData(userData);
};
return (
<>
{isClicked &&
enteredUsername !== props.adminUser[0].username &&
enteredPassword !== props.adminUser[1].password ? (
<SignUp name={enteredUsername} dataTransfer={sendDataToChild} />
) : (
<form onSubmit={onSubmitHandler}>
<h1>W E L C O M E</h1>
<div>
<h1>Login</h1>
<input
type="text"
value={enteredUsername}
placeholder="Enter Username"
onChange={onChangeHandlerUsername}
></input>
<input
type="password"
value={enteredPassword}
placeholder="Enter Password"
onChange={onChangeHandlerPassword}
></input>
<div>
<button>Sign In</button>
</div>
<div>
<button onClick={onClickHandler} type="button">
Sign Up
</button>
</div>
<div>
Forget Password
</div>
</div>
</form>
)}
</>
);
};
export default LoginForm;
HomePage Component:
const HomePage = (props) => {
const name = props.user[0];
const pass = props.user[1];
return (
<div>
<h2>Login Successfull !</h2>
<p>Name: {name}</p>
<p>Password: {pass}</p>
<button type="submit" onClick={props.onLogout}>
Logout
</button>
</div>
);
};
export default HomePage;
Your HomePage user prop is invalid, as you pass a function there (and what's more wrong, you try to get this function's 0th element, as it were an array).
What you should do instead is store the username somewhere (accessible from within your App component), and pass it directly as the HomePage prop.
To give you an idea, your dataFormHandler only returns the first of its two arguments, so in my opinion it's pointless. Your scenario'd work under such conditions:
const myusername = 'mock username';
const mypassword = 'mock password';
return <HomePage onLogout={logoutHandler} user={dataFormHandler(myusername, mypassword)}/>
edit
You jave to modify your loginHandler(username, password), inside it: set the values as state, then do what I wrote earlier.

Testing with jest a register form ( react App)

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!

Resources