React - Merging two Component Values - reactjs

Building a simple ToDo list app in ReactJS. This page is where an existing task can be edited:
import React, {useState, useEffect} from "react";
import {PageLayout} from "../components/page-layout";
import {useParams, useNavigate} from 'react-router-dom';
import TaskDataService from "../services/TaskService";
import DatePicker from 'react-datepicker';
import FadeIn from 'react-fade-in';
import UserDataService from "../services/UserService";
export const Task = props => {
const {id} = useParams();
let navigate = useNavigate();
const initialTaskState = {
id: null,
familyId: "",
userId: "",
ownerId: "",
ownerName: "",
title: "",
description: "",
completed: false,
dueDate: new Date()
};
const [currentTask, setCurrentTask] = useState(initialTaskState);
const [message, setMessage] = useState("");
const [members, setMembers] = useState([]);
const [currentMember, setCurrentMember] = useState(null);
const [currentIndex, setCurrentIndex] = useState(-1);
const getTask = id => {
TaskDataService.get(id)
.then(response => {
setCurrentTask(response.data);
console.log(response.data);
})
.catch(e => {
console.log(e);
});
};
useEffect(() => {
retrieveMembers();
}, []);
useEffect(() => {
if (id)
getTask(id);
}, [id]);
const handleInputChange = event => {
const {name, value} = event.target;
setCurrentTask({...currentTask, [name]: value});
};
const setActiveMember = (member, index) => {
setCurrentMember(member);
setCurrentIndex(index);
};
const retrieveMembers = () => {
UserDataService.listMembers()
.then(response => {
setMembers(response.data);
console.log(response.data);
})
.catch(e => {
console.log(e);
});
};
const updateCompleted = status => {
var data = {
id: currentTask.id,
userId: currentTask.userId,
title: currentTask.title,
ownerId: currentMember.userId,
ownerName: currentMember.firstName,
description: currentTask.description,
completed: status,
dueDate: currentTask.dueDate
};
TaskDataService.update(currentTask.id, data)
.then(response => {
setCurrentTask({...currentTask, completed: status});
console.log(response.data);
})
.catch(e => {
console.log(e);
});
};
const updateTask = () => {
TaskDataService.update(currentTask.id, currentTask)
.then(response => {
console.log(response.data);
setMessage("The task was updated successfully!");
})
.catch(e => {
console.log(e);
});
};
const deleteTask = () => {
TaskDataService.remove(currentTask.id)
.then(response => {
console.log(response.data);
navigate("/tasks");
})
.catch(e => {
console.log(e);
});
};
return (
<PageLayout>
<FadeIn>
<div className="list row">
<div className="col-md-6">
{currentTask ? (
<div className="edit-form">
<h4>Task</h4>
<form>
<div className="form-group">
<label htmlFor="title" class="form-label">Title</label>
<input
type="text"
className="form-control"
id="title"
name="title"
value={currentTask.title}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<label htmlFor="description" class="form-label">Description</label>
<input
type="text"
className="form-control"
id="description"
name="description"
value={currentTask.description}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<label htmlFor="dueDate" class="form-label">Due Date</label>
<DatePicker
onChange={date => handleInputChange({
target: {
value: date.toISOString().split("T")[0],
name: 'dueDate'
}
})}
name="dueDate"
dateFormat="yyyy-MM-dd"
value={currentTask.dueDate.toString().split("T")[0]}
/>
</div>
<div className="form-group">
<label htmlFor="status" className="form-label">
<strong>Status:</strong>
</label>
{currentTask.completed ? " Done" : " Not Done"}
</div>
<ul className="list-group">
<label htmlFor="owner" className="form-label">Task Owner</label>
{members &&
members.map((member, index) => (
<li
className={
"list-group-item " + (index === currentIndex ? "active" : "")
}
onClick={=> setActiveMember(member, index)}
key={index}
>
{member.firstName} {member.lastName}
</li>
))}
</ul>
<div className="col-md-6">
{currentMember ? (
<div>
</div>
) : (
<div>
</div>
)}
</div>
</form>
{currentTask.completed ? (
<button
className="badge text-bg-warning mr-4"
onClick={() => updateCompleted(false)}
>
Not Done?
</button>
) : (
<button
className="badge text-bg-primary mr-2"
onClick={() => updateCompleted(true)}
>
Done!
</button>
)}
<button className="badge text-bg-danger mr-2" onClick={deleteTask}>
Delete
</button>
<button
type="submit"
className="badge text-bg-success"
onClick={updateTask}
>
Update
</button>
<p>{message}</p>
</div>
) : (
<div>
<br/>
<p>Please click on a Task...</p>
</div>
)}
</div>
</div>
</FadeIn>
</PageLayout>
);
};
My problem is with the member selection piece, where you can change ownership of the task:
<ul className="list-group">
<label htmlFor="owner" className="form-label">Task Owner</label>
{members &&
members.map((member, index) => (
<li
className={
"list-group-item " + (index === currentIndex ? "active" : "")
}
onClick={=> setActiveMember(member, index)}
key={index}
>
{member.firstName} {member.lastName}
</li>
))}
</ul>
...and the function where we actually update the task:
const updateTask = () => {
TaskDataService.update(currentTask.id, currentTask)
.then(response => {
console.log(response.data);
setMessage("The task was updated successfully!");
})
.catch(e => {
console.log(e);
});
};
Selecting a new owner from the list did not actually change the ownerId/ownerName value in the task. I have figured out that this is because the new owner values live in currentMember, while the task information lives in currentTask - so I need to figure out how to get information from the updated currentMember into the proper fields in currentTask. I've monkeyed around with configurations but can't find a way to do this. Any advice?

I figured it out. The key was to define the task structure in a variable so I could specify from where the ownerId value would be pulled.
Original:
const updateTask = () => {
TaskDataService.update(currentTask.id, currentTask)
.then(response => {
console.log(response.data);
setMessage("The task was updated successfully!");
})
.catch(e => {
console.log(e);
});
};
Updated:
const updateTask = () => {
var data = {
id: currentTask.id,
userId: currentTask.userId,
title: currentTask.title,
ownerId: currentMember.userId,
ownerName: currentMember.firstName,
description: currentTask.description,
dueDate: currentTask.dueDate
};
TaskDataService.update(currentTask.id, data)
.then(response => {
console.log(response.data);
setMessage("The task was updated successfully!");
})
.catch(e => {
console.log(e);
});
};

Related

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 should I modify React state? addDoc() called with invalid data

I would like to upload a image-file to storage of the firebase and put {title , content, createDate etc} in firestore.
but it seems that state.upload file prevents putting it in firestore.
this.setState({ uploadfile: "" })
The error says
FirebaseError: Function addDoc() called with invalid data. Unsupported field value: a custom File object (found in field uploadfile in document projects/c0hQXdRAgIe1lIzq1vTy)
class CreateProject extends Component {
state = {
title:'',
content:'',
uploadfile:'',
setImageUrl:'',
}
handleChange = (e) =>{
this.setState({
[e.target.id]: e.target.value
})
}
handleSubmit = (e) =>{
e.preventDefault();
this.setState({ uploadfile: "" })
this.props.createProject(this.state)
this.props.history.push('/')
}
onDrop = acceptedFiles => {
if (acceptedFiles.length > 0) {
this.setState({ uploadfile: acceptedFiles[0] })
}
}
handleSubmitImg = (e) =>{
e.preventDefault()
//this.props.sampleteFunction()
};
parseFile = (file) =>{
const updatedFile = new Blob([file], { type: file.type });
updatedFile.name = file.name;
return updatedFile;
}
onSubmit = (event) => {
event.preventDefault();
var updatedFile = this.state.uploadfile;
if (updatedFile === "") {
}
console.log("aaaaaaaaaaaa"+updatedFile)
const uploadTask = storage.ref(`/images/${updatedFile.name}`).put(updatedFile);
uploadTask.on(
firebase.storage.TaskEvent.STATE_CHANGED,
this.next,
this.error,
this.complete
);
};
next = snapshot => {
const percent = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
};
error = error => {
console.log(error);
};
complete = () => {
var updatedFile = this.state.uploadfile
storage
.ref("images")
.child(updatedFile.name)
.getDownloadURL()
.then(fireBaseUrl => {
this.setState({ setImageUrl: fireBaseUrl })
//this.state.setImageUrl(fireBaseUrl);
});
};
render() {
const maxSize = 3 * 1024 * 1024;
const dropzoneStyle = {
width: "100%",
height: "auto",
borderWidth: 2,
borderColor: "rgb(102, 102, 102)",
borderStyle: "dashed",
borderRadius: 5,
}
const {auth} = this.props
console.log("UP"+this.state.uploadfile );
if(!auth.uid) return <Redirect to="/signin" />
return (
<Dropzone
onDrop={this.onDrop}
accept="image/png,image/jpeg,image/gif,image/jpg"
inputContent={(files, extra) => (extra.reject ? 'Image files only' : 'Drag Files')}
styles={dropzoneStyle}
minSize={1}
maxSize={maxSize}
>
{({ getRootProps, getInputProps }) => (
<div className="container">
<form onSubmit={this.handleSubmit} className="white">
<h5 className="grey-text text-darken-3">
Create Project
</h5>
<div className="input-field">
<label htmlFor="title">Title</label>
<input type="text" id="title" onChange={this.handleChange}/>
</div>
<div className="input-field">
<label htmlFor="content">Project Content</label>
<textarea id="content" className="materialize-textarea" onChange={this.handleChange}></textarea>
</div>
<div className="input-field">
<button className="btn pink lighten-1 z-depth-0">Create</button>
</div>
</form>
<div {...getRootProps()}>
<input {...getInputProps()} />
<p>File Drag</p>
{this.state.uploadfile ? <p>Selected file: {this.state.uploadfile.name}</p> : null}
{this.state.uploadfile ? (<Thumb key={0} file={this.state.uploadfile } />) :null}
</div>
<form onSubmit={this.onSubmit}>
<button>Upload</button>
</form>
</div>
)}
</Dropzone>
)
}
}
const matchStateToProps = (state) => {
return{
auth: state.firebase.auth
}
}
const mapDispatchToProps = (dispatch) => {
return{
createProject: (project) => dispatch(createProject(project))
}
}
export default connect(matchStateToProps, mapDispatchToProps)(CreateProject)
Action
export const createProject = (project) => {
return (dispatch, getState, { getFirebase, getFirestore }) => {
// make async call to database
const firestore = getFirestore(); 
const profile = getState().firebase.profile;
const authorId = getState().firebase.auth.uid;
firestore.collection('projects').add({
...project,
authorUserName: profile.userName,
authorId: authorId,
createdAt: new Date()
}).then(() => {
dispatch({ type: 'CREATE_PROJECT', project})
}).catch((err) => {
dispatch({ type: 'CREATE_PROJECT_ERROR', err })
})
}
};

React state prevents uploading in react-redux

How Should I solve this problem?
After uploading an image by react state, this state seems to prevents putting props"project" in Action in Redux.
this.setState({ uploadfile: "" })
The error says
FirebaseError: Function addDoc() called with invalid data. Unsupported field value: a custom File object (found in field uploadfile in document projects/c0hQXdRAgIe1lIzq1vTy)
class CreateProject extends Component {
state = {
title:'',
content:'',
uploadfile:'',
setImageUrl:'',
}
handleChange = (e) =>{
this.setState({
[e.target.id]: e.target.value
})
}
handleSubmit = (e) =>{
e.preventDefault();
this.setState({ uploadfile: "" })
this.props.createProject(this.state)
this.props.history.push('/')
}
onDrop = acceptedFiles => {
if (acceptedFiles.length > 0) {
this.setState({ uploadfile: acceptedFiles[0] })
}
}
handleSubmitImg = (e) =>{
e.preventDefault()
//this.props.sampleteFunction()
};
parseFile = (file) =>{
const updatedFile = new Blob([file], { type: file.type });
updatedFile.name = file.name;
return updatedFile;
}
onSubmit = (event) => {
event.preventDefault();
var updatedFile = this.state.uploadfile;
if (updatedFile === "") {
}
console.log("aaaaaaaaaaaa"+updatedFile)
const uploadTask = storage.ref(`/images/${updatedFile.name}`).put(updatedFile);
uploadTask.on(
firebase.storage.TaskEvent.STATE_CHANGED,
this.next,
this.error,
this.complete
);
};
next = snapshot => {
const percent = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
};
error = error => {
console.log(error);
};
complete = () => {
var updatedFile = this.state.uploadfile
storage
.ref("images")
.child(updatedFile.name)
.getDownloadURL()
.then(fireBaseUrl => {
this.setState({ setImageUrl: fireBaseUrl })
//this.state.setImageUrl(fireBaseUrl);
});
};
render() {
const maxSize = 3 * 1024 * 1024;
const dropzoneStyle = {
width: "100%",
height: "auto",
borderWidth: 2,
borderColor: "rgb(102, 102, 102)",
borderStyle: "dashed",
borderRadius: 5,
}
const {auth} = this.props
console.log("UP"+this.state.uploadfile );
if(!auth.uid) return <Redirect to="/signin" />
return (
<Dropzone
onDrop={this.onDrop}
accept="image/png,image/jpeg,image/gif,image/jpg"
inputContent={(files, extra) => (extra.reject ? 'Image files only' : 'Drag Files')}
styles={dropzoneStyle}
minSize={1}
maxSize={maxSize}
>
{({ getRootProps, getInputProps }) => (
<div className="container">
<form onSubmit={this.handleSubmit} className="white">
<h5 className="grey-text text-darken-3">
Create Project
</h5>
<div className="input-field">
<label htmlFor="title">Title</label>
<input type="text" id="title" onChange={this.handleChange}/>
</div>
<div className="input-field">
<label htmlFor="content">Project Content</label>
<textarea id="content" className="materialize-textarea" onChange={this.handleChange}></textarea>
</div>
<div className="input-field">
<button className="btn pink lighten-1 z-depth-0">Create</button>
</div>
</form>
<div {...getRootProps()}>
<input {...getInputProps()} />
<p>File Drag</p>
{this.state.uploadfile ? <p>Selected file: {this.state.uploadfile.name}</p> : null}
{this.state.uploadfile ? (<Thumb key={0} file={this.state.uploadfile } />) :null}
</div>
<form onSubmit={this.onSubmit}>
<button>Upload</button>
</form>
</div>
)}
</Dropzone>
)
}
}
const matchStateToProps = (state) => {
return{
auth: state.firebase.auth
}
}
const mapDispatchToProps = (dispatch) => {
return{
createProject: (project) => dispatch(createProject(project))
}
}
export default connect(matchStateToProps, mapDispatchToProps)(CreateProject)
Action
export const createProject = (project) => {
return (dispatch, getState, { getFirebase, getFirestore }) => {
// make async call to database
const firestore = getFirestore(); 
const profile = getState().firebase.profile;
const authorId = getState().firebase.auth.uid;
firestore.collection('projects').add({
...project,
authorUserName: profile.userName,
authorId: authorId,
createdAt: new Date()
}).then(() => {
dispatch({ type: 'CREATE_PROJECT', project})
}).catch((err) => {
dispatch({ type: 'CREATE_PROJECT_ERROR', err })
})
}
};

TypeError: formData.set is not a function

Hii i am getting an error when i am trying to filling a value in the form then getting error like "form data is not a function" dont know whats going on wrong please help as soon as possible
error img https://ibb.co/xMy002L
addmovie.js
here is my addmovie form where i wrote my whole logic
import React,{useState} from 'react';
import Navbar from '../pages/Navbar';
import Footer from '../pages/Footer';
import {Link} from 'react-router-dom';
import {isAuthenticated} from '../Auth/index';
import {addMovie} from '../Admin/adminapicall';
const AddMovie = () => {
const {user,token} = isAuthenticated();
const [values,setValues] = useState({
movie_name:'',
actor:'',
country_of_origin:'',
duration:'',
director:'',
photo:'',
loading:false,
error:'',
addedMovie:'',
getRedirect:false,
formData:''
})
const {movie_name,actor,country_of_origin,duration,director,photo,loading,error,addedMovie,getRedirect,formData} = values;
const handleChange = name => event => {
const value = name === "photo" ? event.target.files[0] : event.target.value
formData.set(name,value);
setValues({...values,[name]:value})
};
const onSubmit = (e) => {
e.preventDefault();
setValues({...values,error:'',loading:true})
addMovie(user._id,token,formData).then(data =>{
if(data.error){
setValues({...values,error:data.error})
}else{
setValues({
...values,
movie_name:'',
actor:'',
country_of_origin:'',
duration:'',
director:'',
photo:'',
loading:false,
addedMovie: data.movie_name
})
}
})
}
const successMessage = () => (
<div className='alert alert-success mt-3'
style={{display : addedMovie ? '' : 'none'}}>
<h4>{addedMovie} added successfully</h4>
</div>
)
// const successMessage = () => {
// }
const addMovieForm = () => (
<form >
<span>Post photo</span>
<div className="form-group">
<label className="btn btn-block btn-success">
<input
onChange={handleChange("photo")}
type="file"
name="photo"
accept="image"
placeholder="choose a file"
/>
</label>
</div>
<div className="form-group">
<input
onChange={handleChange("movie_name")}
name="photo"
className="form-control"
placeholder="movie_name"
value={movie_name}
/>
</div>
<div className="form-group">
<input
onChange={handleChange("actor")}
name="photo"
className="form-control"
placeholder="actor"
value={actor}
/>
</div>
<div className="form-group">
<input
onChange={handleChange("duration")}
type="number"
className="form-control"
placeholder="duration"
value={duration}
/>
</div>
<div className="form-group">
<input
onChange={handleChange("country_of_origin")}
type="text"
className="form-control"
placeholder="country_of_origin"
value={country_of_origin}
/>
</div>
<div className="form-group">
<input
onChange={handleChange("director")}
type="text"
className="form-control"
placeholder="director"
value={director}
/>
</div>
<button type="submit" onClick={onSubmit} className="btn btn-success mb-2">
Add Movie
</button>
</form>
);
return (
<div>
<Navbar/>
<div className='container'style={{height:'0px'}}>
<Link to='/admin/dashboard'> <h1 className=' bg-info text-white p-4 text-decoration-none'>Admin Home</h1> </Link>
<div className='row bg-dark text-white rounded'>
<div className='col-md-8 offset-md-2'>
{successMessage()}
{addMovieForm()}
</div>
</div>
</div>
<Footer/>
</div>
)
}
export default AddMovie;
adminapicall.js
this is code where my frontend talk with backend
import {API} from '../backend';
//products calls
//add movie
export const addMovie = (userId,token,movie)=>{
return fetch(`${API}/movie/addMovie/${userId}`,{
method : "POST",
headers:{
Accept:'Application/json',
Authorization: `Bearer ${token}`
},
body:movie
}).then(response => {
return response.json()
})
.catch(err => console.log(err))
}
//get all movie
export const getAllMovies = () => {
return fetch(`${API}/movies`,{
method : "GET"
})
.then(response => {
return response.json();
})
.catch(err => console.log(err))
}
//get a movie
export const getMovie = movieId =>{
return fetch(`${API}/movie/${movieId}`,{
method : "GET"
})
.then(response => {
return response.json();
})
.catch(err => console.log(err))
}
//update movie
export const updateMovie = (movieId,userId,token,movie)=>{
return fetch(`${API}/movie/${movieId}/${userId}`,{
method : "PUT",
headers:{
Accept:'Application/json',
Authorization: `Bearer ${token}`
},
body:movie
}).then(response => {
return response.json()
})
.catch(err => console.log(err))
}
//delete movie
export const deleteMovie = (movieId,userId,token)=>{
return fetch(`${API}/movie/${movieId}/${userId}`,{
method : "DELETE",
headers:{
Accept:'Application/json',
Authorization: `Bearer ${token}`
}
}).then(response => {
return response.json()
})
.catch(err => console.log(err))
}
i think ur mistaken here,
const [values,setValues] = useState({
movie_name:'',
actor:'',
country_of_origin:'',
duration:'',
director:'',
photo:'',
loading:false,
error:'',
addedMovie:'',
getRedirect:false,
formData:'' // <-
})
const {movie_name,actor,country_of_origin,duration,director,photo,loading,error,addedMovie,getRedirect,formData} = values;
const handleChange = name => event => {
const value = name === "photo" ? event.target.files[0] : event.target.value
formData.set(name,value); // <-
setValues({...values,[name]:value})
};
const onSubmit = (e) => {
e.preventDefault();
setValues({...values,error:'',loading:true})
addMovie(user._id,token,formData).then(data =>{
// ^^^^^^^^ <-
if(data.error){
setValues({...values,error:data.error})
}else{
setValues({
...values,
movie_name:'',
actor:'',
country_of_origin:'',
duration:'',
director:'',
photo:'',
loading:false,
addedMovie: data.movie_name
})
}
})
You might wanted to do somethig like this,
const [values,setValues] = useState({
movie_name:'',
actor:'',
country_of_origin:'',
duration:'',
director:'',
photo:'',
loading:false,
error:'',
addedMovie:'',
getRedirect:false,
})
const {movie_name,actor,country_of_origin,duration,director,photo,loading,error,addedMovie,getRedirect} = values;
const handleChange = name => event => {
const value = name === "photo" ? event.target.files[0] : event.target.value
setValues({...values,[name]:value})
};
const onSubmit = (e) => {
e.preventDefault();
setValues({...values,error:'',loading:true})
addMovie(user._id,token,JSON.stringify(values)).then(data =>{
if(data.error){
setValues({...values,error:data.error})
}else{
setValues({
...values,
movie_name:'',
actor:'',
country_of_origin:'',
duration:'',
director:'',
photo:'',
loading:false,
addedMovie: data.movie_name
})
}
})
const [values,setValues] = useState({
movie_name:'',
actor:'',
country_of_origin:'',
duration:'',
director:'',
photo:'',
loading:false,
error:'',
addedMovie:'',
getRedirect:false,
formData:new FormData() <---- declare form, data like this
})
I know it's late but according to my study,
we need to check if we are on a server-side environment or client environment (browser).
we can check(for client-side), (process.browser == true) but since now it is deprecated we can use
**(typeof window !== 'undefined')**
const [values, setValues] = useState({
formData: typeof window !== 'undefined' && new FormData(),
// other values
});
Refer to https://github.com/zeit/next.js/issues/5354#issuecomment-520305040
Also,
If you're using Next.js newer versions, you can use getStaticProps or getServerSideProps instead of getInitialProps.

How to rerender DOM after component's state was changed?

I am making a request to axios and receiving some data, which then I setState to my component's state:
componentDidMount() {
instance
.get("https://bartering-application.firebaseio.com/myitems.json")
.then(response => {
var obj = Object.values(response.data);
console.log("parsed", obj);
this.setState({ addedItem: obj });
})
.catch(error => {
console.log(error);
});
}
So my state, which had state property addedItem now gets objs as value.
Then, in my render() method I am rendering a child component, which receives props from my state(whose properties updated through componentDidMount):
render() {
const items = this.state.addedItem.map(item => {
return (
<MyItem
title={item.Title}
description={item.Description}
condition={item.Condition}
url={item.URL}
/>
)
})
}
This works fine, however I can see the result of child component displayed, only if I reload the browser. How can I make the app reload automatically whenever a state property (in my case addedItem) changes ? Which lifecycle method should I use to rerender the DOM immidiately when the state property changes ?
The full component code is below:
class MyItems extends Component {
constructor(props) {
super(props);
const initial_state = {
image: null,
url: "",
uploadStatus: false,
itemTitle: "",
itemDescription: "",
barteringCondition: "",
addedItem: []
};
this.state = initial_state;
this.handleChange = this.handleChange.bind(this);
this.handleUpload = this.handleUpload.bind(this);
}
componentDidMount() {
instance
.get("https://bartering-application.firebaseio.com/myitems.json")
.then(response => {
var obj = Object.values(response.data);
console.log("parsed", obj);
this.setState({ addedItem: obj });
})
.catch(error => {
console.log(error);
});
}
// componentDidUpdate(prevState){
// if (prevState !== this.state){
// window.location.reload();
// }
// }
handleChange = e => {
if (e.target.files[0]) {
const image = e.target.files[0];
this.setState(
() => ({ image, uploadStatus: true }),
() => console.log(this.state.image.name)
);
}
};
handleUpload = () => {
if (!this.state.uploadStatus) {
alert("No item image was uploaded.");
return null;
}
const { image } = this.state;
const uploadTask = storage.ref(`images/${image.name}`).put(image);
uploadTask.on(
"state_changed",
snapshot => {
// demonstrate the image upload progress
},
error => {
// error function
console.log(error);
},
() => {
//complete function
storage
.ref(`images`)
.child(image.name)
.getDownloadURL()
.then(url => {
console.log(url);
alert("uploaded!");
this.setState({ url });
// When uploadded image url is received, collect all item data into myNewItem object and post this record to Firebase Database
const myNewItem = {
Title: this.state.itemTitle,
Description: this.state.itemDescription,
URL: this.state.url,
Condition: this.state.barteringCondition
};
instance.post("/myitems.json", myNewItem).then(error => {
console.log(error);
});
});
}
);
};
titleChangeHandler = event => {
this.setState({ itemTitle: event.target.value });
};
descriptionChangeHandler = event => {
this.setState({ itemDescription: event.target.value });
};
render() {
const items = this.state.addedItem.map(item => {
return (
<MyItem
title={item.Title}
description={item.Description}
condition={item.Condition}
url={item.URL}
/>
);
});
return (
<Auxiliary>
<div className={classes.MyItems}>
<div className={classes.container}>
<div className={classes.MyItems__left__container}>
<div className={classes.Items__Upload}>
{" "}
<p>Upload your barter item picture below:</p>
<br />
<input type="file" onChange={this.handleChange} />
<br />
<p style={{ padding: "0px", margin: "10px" }}>
Title of the item:
</p>
<input type="text" onChange={this.titleChangeHandler} />
</div>
<div className={classes.Items__Info}>
<div className={classes.Items_Description}>
<p>Describe your item:</p>
<textarea
rows="15"
cols="30"
onChange={this.descriptionChangeHandler}
/>
</div>
<div className={classes.Items_Bartering__Condition}>
<p>Bartering condition:</p>
<br />
<div className={classes.Items__Bartering_Condition_Options}>
<fieldset id="barter-options">
<input type="radio" name="with-similar" />
With a similar item <br />
<input type="radio" name="with-similar-with-extra" />
With a similar item with extra payment <br />
<input type="radio" name="with" />
With
<input
style={{ height: "11px", maxWidth: "240px" }}
type="text"
name="special-item"
placeholder="e.g. Rolex Watch model 16233"
/>
<br />
<input type="radio" name="as-gift" />I give this item as
gift! <br />
<input type="radio" name="as-gift" />I give this item as
gift to
<input
style={{ height: "11px", maxWidth: "120px" }}
type="text"
placeholder="e.g. students"
/>
<br />
</fieldset>
<div className={classes.Items_addButton}>
<button onClick={this.handleUpload}>+ADD</button>
</div>
</div>
</div>
</div>
</div>
<div className={classes.MyItems__right__container}>
<div className={classes.MyItems__right__container__header}>
<p>My items</p>
</div>
<div className={classes.MyItems__right__container__block}>
{/* <MyItem title={this.state.itemTitle} description={this.state.itemDescription} condition={this.state.barteringCondition} url={this.state.url} /> */}
{items}
</div>
</div>
</div>
</div>
</Auxiliary>
);
}
}
export default MyItems;
the child MyItem component:
import React, { useEffect } from "react";
import Auxiliary from "../hoc/Auxiliary";
import { storage } from "../Firebase/Fire";
import classes from "../MyItem/MyItem.module.css";
const MyItem = props => {
return (
<Auxiliary>
<div className={classes.MyItem}>
<h4>Item: {props.title}</h4>
<img
src={props.url || "https://via.placeholder.com/140x100"}
height="100"
width="140"
/>
<p>Description: {props.description}</p>
<p>Bartering condition: {props.condition}</p>
</div>
</Auxiliary>
);
};
export default MyItem;
I solved this by adding window.location.reload() method, which triggers the rerendering of DOM immediately after the user image was successfully uploaded to the FIrebase Database. So here is the code:
instance.post('/myitems.json', myNewItem)
.then(response => {window.location.reload();} )
.then(error => {
console.log(error);
})
Firebase will gives you the response in the form of object. We need to convert this to array and use it. Try the below logic at the start of your render function:
const fetchedItems = [];
for(let key in this.state.addedItem) {
fetchedItems.push({
...this.state.addedItem[key],
id: key
});
}
const items = fetchedItems.map(item => {
return (
<MyItem
title={item.Title}
description={item.Description}
condition={item.Condition}
url={item.URL}
/>
);
});

Resources