How to create nested comments? - javascript

Somebody correct me please, I can't create nested comments in the app.
I use node.js express and react.js
At the final,
I can add only parent comments, but not reply comment. How should I edit my code?
Any help would be appreciated. Thank you.
here is my backend model code
const { Schema, model } = require("mongoose");
const Joi = require("joi");
const { handleMongooseSchemaError } = require("../helpers");
const commentSchema = new Schema(
{
user: {
type: String,
// required: true,
},
text: {
type: String,
required: true,
},
parentId: {
type: Schema.Types.ObjectId,
ref: 'Comment',
default: null
},
owner: {
type: Schema.Types.ObjectId,
ref: "user",
required: true,
},
replies: [
{
type: Schema.Types.ObjectId,
ref: 'Comment',
}
],
},
{ versionKey: false, timestamps: true }
);
commentSchema.post("save", handleMongooseSchemaError);
const addCommentSchema = Joi.object({
text: Joi.string().required().min(7),
});
const schemas = {
addCommentSchema,
};
const Comment = model("Comment", commentSchema);
module.exports = {
Comment,
schemas,
};
I'm getting this json in frontend
[
{
_id:63d3efbb8dd7eb9de001dd71
text:"Lorem text dgdgdgdggdgd"
parentId:null
owner:63d07abf6f39d8a6a5a27d10
replies:Array[]
createdAt:2023-01-27T15:37:31.690+00:00
updatedAt:2023-01-27T15:37:31.690+00:00
},
]
and here is my frontend code. commentsList
import React, { useEffect, useState } from 'react';
import { useDispatch } from 'react-redux';
import { FaReply } from "react-icons/fa"
import { addComment, fetchComments } from '../../redux/comment/comment-operation';
import Comment from './Comment';
import CommentForm from './CommentForm';
import s from "./comments.module.scss"
const Comments = ({ comments }) => {
const dispatch = useDispatch();
const [modalFormOpen, setModalFormOpen] = useState(false);
function showFormModal() {
setModalFormOpen(true);
}
function closeFormModal() {
setModalFormOpen(false);
}
const rootComments = comments.filter((comment) => comment.parentId === null);
const getReplies = commentId => {
return comments.filter((comment) => comment.parentId === commentId)
}
useEffect(() => {
dispatch(fetchComments());
}, [dispatch]);
const onAddComment = (data) => {
dispatch(addComment(data));
closeFormModal()
}
return (
<div className={s.comments}>
<div className={s.commentActions}>
<FaReply
className='icon'
onClick={showFormModal}
aria-label="Reply"
/>
{modalFormOpen && (<div close={'closeFormModal'}>
<CommentForm submitLabel="Write" onSubmit={onAddComment} onClick={closeFormModal} />
</div>
)}
</div>
<div className={s.commentsContainer}>
{rootComments.map(rootComment => (
<Comment key={rootComment._id}
comment={rootComment}
replies={getReplies(rootComment._id)}
addComment={onAddComment}
currentUserId={rootComment.owner._id}
/>
))}
</div>
</div >
);
}
export default Comments;
comment reply
import { useState } from "react";
import { FaReply } from "react-icons/fa"
import { useSelector } from 'react-redux';
import { getUser } from '../../redux/auth/auth-selectors';
import CommentForm from './CommentForm';
import s from "./comments.module.scss";
const dataFormatter = new Intl.DateTimeFormat(undefined, {
dateStyle: "medium",
timeStyle: "short"
})
function Comment({ comment, replies, addComment, currentUserId }) {
const { avatarURL } = useSelector(getUser);
const [secondModalOpen, setSecondModalOpen] = useState(false);
function showSecondModal() {
setSecondModalOpen(true);
}
function closeSecondModal() {
setSecondModalOpen(false);
}
const addSecondCommit = (data) => {
addComment(data)
closeSecondModal()
}
return (
<div className={s.comment}>
<div className={s.commentNextPart}>
<div className="comment-content">
<div className="comment-author">{comment.user}</div>
</div>
<div className={s.commentText}>
<img src={avatarURL} alt="Avatar" style={{
width: 30,
height: 30,
borderRadius: '50%',
}} />
<span className={s.name}>{comment.owner.name}</span>
{dataFormatter.format(Date.parse(comment.createdAt))}
</div>
<div className={s.message}>{comment.text}</div>
<div className={s.commentActions}>
<FaReply
className='icon'
onClick={showSecondModal}
aria-label="Reply"
/>
{secondModalOpen && (<div close={'closeSecondModal'}>
<CommentForm submitLabel="Write" onSubmit={addSecondCommit} onClick={closeSecondModal} />
</div>
)}
</div>
{replies.length > 0 && (
<div className={s.replies}>
{replies.map(reply => (
<Comment
comment={reply}
key={reply._id}
replies={[]}
parentId={comment._id}
currentUserId={currentUserId}
/>
))}
</div>
)}
</div>
</div>
)
}
export default Comment

Related

Cannot get entityMap using #draft-js-plugins/linkify & hashtag plugin (NextJS)

I'm not getting any kind of entity data for Mention or Hashtag while using #draft-js-plugins. The link entity only working if I paste the URL directly on the editor. Mention is working fine but the hashtag or URL entity not working.
Hashtag and URL are detected properly on the editor but the editorState is not returning the entity data for hashtag and URL on typing.
import React, { ReactElement, useCallback, useMemo, useRef, useState } from 'react';
import { convertToRaw, EditorState } from 'draft-js';
import Editor, { createEditorStateWithText } from '#draft-js-plugins/editor';
import createHashtagPlugin from '#draft-js-plugins/hashtag';
import createLinkifyPlugin, { extractLinks } from '#draft-js-plugins/linkify';
import tlds from 'tlds';
import linkifyIt from 'linkify-it';
import createMentionPlugin, { defaultSuggestionsFilter } from '#draft-js-plugins/mention';
import editorStyles from './lib/styles/EditorStyles.module.css';
import hashtagStyles from './lib/styles/HashtagStyles.module.css';
import mentionStyles from './lib/styles/MentionStyles.module.css';
import linkStyles from './lib/styles/LinkStyles.module.css';
import '#draft-js-plugins/mention/lib/plugin.css';
import mentions from './lib/data/mentions';
import { EntryComponentProps } from '#draft-js-plugins/mention/lib/MentionSuggestions/Entry/Entry';
// mention suggestion component
function Entry(props: EntryComponentProps): ReactElement {
const { mention, theme, searchValue, isFocused, ...parentProps } = props;
return (
<div {...parentProps}>
<div className={theme?.mentionSuggestionsEntryContainer}>
<div className={theme?.mentionSuggestionsEntryContainerLeft}>
<img
src={mention.avatar}
className={theme?.mentionSuggestionsEntryAvatar}
role='presentation'
/>
</div>
<div className={theme?.mentionSuggestionsEntryContainerRight}>
<div className={theme?.mentionSuggestionsEntryText}>{mention.name}</div>
<div className={theme?.mentionSuggestionsEntryTitle}>{mention.title}</div>
</div>
</div>
</div>
);
}
export default function DynamicPostInputArea({
editorKey = 'comment',
placeholder = 'Write a comment',
}) {
const ref = useRef<Editor>(null);
// editor state
const [editorState, setEditorState] = useState(EditorState.createEmpty());
const [open, setOpen] = useState(false);
const [suggestions, setSuggestions] = useState(mentions);
// =================================================================
// == set plugins & configuration settings for the dynamic post input area
// =================================================================
const { plugins, MentionSuggestions } = useMemo(() => {
// link detection plugin
const linkifyPlugin = createLinkifyPlugin({
customExtractLinks: (text) => linkifyIt().tlds(tlds).set({ fuzzyEmail: false }).match(text),
component(props) {
// eslint-disable-next-line no-alert, jsx-a11y/anchor-has-content
return <a style={{ color: 'var(--link)' }} {...props} />;
},
});
// hashtag detection plugin
const hashtagPlugin = createHashtagPlugin({
theme: hashtagStyles,
});
// mention detection plugin
const mentionPlugin = createMentionPlugin({
// entityMutability: 'IMMUTABLE',
supportWhitespace: true,
});
const { MentionSuggestions } = mentionPlugin;
const plugins = [hashtagPlugin, linkifyPlugin, mentionPlugin];
return { plugins, MentionSuggestions };
}, []);
// =================================================================
// == mention modifier
// =================================================================
const onOpenChange = useCallback((_open: boolean) => {
setOpen(_open);
}, []);
const onSearchChange = useCallback(({ value }: { value: string }) => {
setSuggestions(defaultSuggestionsFilter(value, mentions));
}, []);
// =================================================================
// == on change of editor inputs this function will be called
// =================================================================
const onChange = useCallback((_editorState: EditorState) => {
setEditorState(_editorState);
const contentState = _editorState.getCurrentContent();
const inputData = {
text: _editorState.getCurrentContent().getPlainText('\u0001'),
content: convertToRaw(contentState),
entityMap: contentState.getAllEntities(),
};
console.log(inputData);
}, []);
return (
<>
<div
className={editorStyles.editor}
onClick={() => {
ref.current!.focus();
}}>
<Editor
ref={ref}
placeholder={placeholder}
editorState={editorState}
editorKey={editorKey}
onChange={onChange}
// onChange={setEditorState}
plugins={plugins}
// decorators={}
// keyBindingFn={bindedKeys}
/>
<MentionSuggestions
open={open}
onOpenChange={onOpenChange}
suggestions={suggestions}
onSearchChange={onSearchChange}
// onAddMention={() => {
// // get the mention object selected
// }}
// entryComponent={Entry}
popoverContainer={({ children }) => <div>{children}</div>}
/>
</div>
<style jsx global>{`
.public-DraftEditorPlaceholder-root {
position: absolute;
}
.public-DraftEditorPlaceholder-root > * {
color: var(--outline);
font-size: 0.875rem;
}
`}</style>
</>
);
}
I need some data like this for every hashtags & URLs
...
entityRanges: [
{
offset: 7,
length: 14,
key: 2,
},
{
offset: 25,
length: 7,
key: 3,
}]
...
entityMap: {
'2': {
type: 'LINK',
mutability: 'MUTABLE',
data: {
href: 'http://helloworld.com/',
rel: 'noreferrer noopener',
target: '_self',
url: 'http://helloworld.com/',
},
},
'3': {
type: 'LINK',
mutability: 'MUTABLE',
data: {
href: 'http://abcd.co/',
rel: 'noreferrer noopener',
target: '_self',
url: 'http://abcd.co/',
},
},
}
...
If anyone can help me solve this it'll be a great help for me. Thank you.

React hooks form, setting default values from a reduced array doesn't populate, but manually enterring same object does

I am using react hooks forms, and I am trying to set the default values of a form that is outputted by mapping over an array and outputting the inputs in the form. I have reduced the array to an object like this {name0:"fijs",name1:"3838"...} and if I manually pass that in the default values it maps to my inputs and populates them. However if I enter them from the variable that is doing the reduce function it doesn't populate it. I think it is because on first render it is undefined. I have tried using a useEffect, but that didn't work so I am stuck.
This is the part of the code I am working on
const test = formState?.reduce((obj, item, idx) => {
return { ...obj, [`${item.name}${idx}`]: "fdsjfs" };
}, {});
const { register, handleSubmit, errors } = useForm({
defaultValues: test,
});
console.log(test);
and this is the whole thing
import { useQuery, gql, useMutation } from "#apollo/client";
import { useEffect, useState } from "react";
import { v4 as uuidv4 } from "uuid";
import { useForm } from "react-hook-form";
const INPUT_VALUES = gql`
query GetInputValues {
allFormInputVals {
data {
name
_id
type
}
}
}
`;
const ADD_INPUT_VALUES = gql`
mutation AddInputValues(
$name: String!
$type: String!
$index: Int!
$ID: ID!
) {
createFormInputVal(
data: {
name: $name
type: $type
index: $index
formRoot: { connect: $ID }
}
) {
name
}
}
`;
const Home = () => {
const blankFormInput = {
__typename: "FormInputVal",
name: "test",
_id: uuidv4(),
type: "text",
};
const [formState, setFormState] = useState([blankFormInput]);
const [formStateVals, setFormStateVals] = useState(undefined);
const { loading, error, data } = useQuery(INPUT_VALUES);
const [createFormInputVal, { data: createInputData }] = useMutation(
ADD_INPUT_VALUES
);
useEffect(() => {
setFormState(data?.allFormInputVals?.data);
}, [data]);
const test = formState?.reduce((obj, item, idx) => {
return { ...obj, [`${item.name}${idx}`]: "fdsjfs" };
}, {});
const { register, handleSubmit, errors } = useForm({
defaultValues: test,
});
console.log(test);
const onSubmit = (data) => console.log(data);
console.log(errors);
const addInput = async () => {
const blanktext = {
__typename: "FormInputVal",
name: "Product Image",
_id: uuidv4(),
type: "text",
};
setFormState([...formState, { ...blanktext }]);
console.log(formState);
const res = await createFormInputVal({
variables: {
name: "test",
type: "text",
index: 0,
ID: "291541554941657608",
},
}).catch(console.error);
console.log(res);
};
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<>
<form onSubmit={handleSubmit(onSubmit)}>
<input type="button" value="Add Form Input" onClick={addInput} />
{formState?.map((val, idx) => {
const nameId = `name${idx}`;
const typeId = `type-${idx}`;
return (
<div key={val._id}>
{val.type === "text" && (
<>
<label htmlFor={nameId}>{`${val.name} #${idx + 1}`}</label>
<input
type="text"
name={nameId}
id={nameId}
className={val.type}
ref={register()}
/>
{/* <label htmlFor={typeId}>{`Type #${idx + 1}`}</label>
<select name={typeId} id={typeId} className={val.type}>
{data.allFormInputVals.data.map((item) => {
return (
<option key={item._id} value={item.type}>
{item.type}
</option>
);
})}
</select> */}
</>
)}
</div>
);
})}
<button type="submit">Save Form</button>
</form>
</>
);
};
export default Home;
UPDATE: I have tried useEffect with a reset from the api, I thought this was the solution, but still no dice.
const { register, handleSubmit, errors, reset } = useForm();
useEffect(() => {
const result = test; // result: { firstName: 'test', lastName: 'test2' }
reset(result); // asynchronously reset your form values
}, [reset]);
UPDATE: I abstracted the Form to it;s own component, but it still does not work.
Form.js
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { useQuery, gql, useMutation } from "#apollo/client";
import { v4 as uuidv4 } from "uuid";
const ADD_INPUT_VALUES = gql`
mutation AddInputValues(
$name: String!
$type: String!
$index: Int!
$ID: ID!
) {
createFormInputVal(
data: {
name: $name
type: $type
index: $index
formRoot: { connect: $ID }
}
) {
name
}
}
`;
export default function Form({ formState, setFormState }) {
const test = formState?.reduce((obj, item, idx) => {
return { ...obj, [`${item.name}${idx}`]: "fdsjfs" };
}, {});
console.log(test);
const { register, handleSubmit, errors } = useForm({ defaultValues: test });
const [formStateVals, setFormStateVals] = useState(undefined);
// console.log(test);
const onSubmit = (data) => console.log(data);
console.log(errors);
const addInput = async () => {
const blanktext = {
__typename: "FormInputVal",
name: "Product Image",
_id: uuidv4(),
type: "text",
};
setFormState([...formState, { ...blanktext }]);
console.log(formState);
const res = await createFormInputVal({
variables: {
name: "test",
type: "text",
index: 0,
ID: "291541554941657608",
},
}).catch(console.error);
console.log(res);
};
const [createFormInputVal, { data: createInputData }] = useMutation(
ADD_INPUT_VALUES
);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input type="button" value="Add Form Input" onClick={addInput} />
{formState?.map((val, idx) => {
const nameId = `name${idx}`;
const typeId = `type-${idx}`;
return (
<div key={val._id}>
{val.type === "text" && (
<>
<label htmlFor={nameId}>{`${val.name} #${idx + 1}`}</label>
<input
type="text"
name={nameId}
id={nameId}
className={val.type}
ref={register()}
/>
{/* <label htmlFor={typeId}>{`Type #${idx + 1}`}</label>
<select name={typeId} id={typeId} className={val.type}>
{data.allFormInputVals.data.map((item) => {
return (
<option key={item._id} value={item.type}>
{item.type}
</option>
);
})}
</select> */}
</>
)}
</div>
);
})}
<button type="submit">Save Form</button>
</form>
);
}
index.js
import { useQuery, gql, useMutation } from "#apollo/client";
import { useEffect, useState } from "react";
import { v4 as uuidv4 } from "uuid";
import Form from "../components/Form";
const INPUT_VALUES = gql`
query GetInputValues {
allFormInputVals {
data {
name
_id
type
}
}
}
`;
const Home = () => {
const blankFormInput = {
__typename: "FormInputVal",
name: "test",
_id: uuidv4(),
type: "text",
};
const [formState, setFormState] = useState([blankFormInput]);
const { loading, error, data } = useQuery(INPUT_VALUES);
useEffect(() => {
const formData = data?.allFormInputVals?.data;
setFormState(formData);
}, [data]);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<>
<Form formState={formState} setFormState={setFormState} />
</>
);
};
export default Home;
You could extract the form to its own component and only render it when the data is fetched. This way, when you use useForm in the child component, the default values will be set properly.
const Home = () => {
const { loading, error, data } = useQuery(INPUT_VALUES)
const blankFormInput = {
__typename: "FormInputVal",
name: "test",
_id: uuidv4(),
type: "text",
}
const [formState, setFormState] = useState([blankFormInput])
// other code
if (loading) {
return <p>Loading...</p>
}
return <MyForm defaultValues={formState} />
}
If you don't want to change the structure, you could set the input values using setValue when the data is ready.
useEffect(() => {
const formData = data?.allFormInputVals?.data
setFormState(formData)
formData?.forEach((item, idx) => {
setValue(`${item.name}${idx}`, 'whatever')
})
}, [data])

adding counter to redux state

I am trying to add a like increment count using redux to my app, but I am not what I have to do I have been tampering with it for the last few days and have gotten nowhere. I wanted to use my comments from my shared folder as the state that I wanted to alter on the store. Any help would be appreciated! Thanks
this is my store:
import { createStore, combineReducers } from 'redux';
import { Comments } from './comments';
import { counterReducer } from './counter'
export const ConfigureStore = () => {
const store = createStore(
combineReducers({
counter : counterReducer,
comments: Comments
}
));
return store;
};
action creators:
import * as ActionTypes from './ActionTypes';
export const addComment = (commentId, author, text, likes, date) => ({
type: ActionTypes.ADD_COMMENT,
payload: {
commentId : commentId,
author: author,
text: text,
likes: likes,
date: date
}
});
export const likeComment = (commentId, likes) => ({
type: ActionTypes.LIKE_COMMENT,
payload: {
commentId: commentId,
likes: likes
}
});
my counter reducer for the likes (i am using my COMMENTS as my state that has the commentId, comments, author, dates, likes):
import * as ActionTypes from './ActionTypes';
import { COMMENTS } from '../shared/comments';
export const counterReducer = (state = COMMENTS, action) => {
switch(action.type) {
case ActionTypes.LIKE_COMMENT:
const comment = action.payload
comment.id = state.length
comment.like = state.likes + 1
return state.concat(comment)
default:
return state;
}
};
This is the main component:
import React, { Component } from 'react';
import TopComment from './TopComment';
import DisplayComment from './PostComment';
import { withRouter } from 'react-router-dom';
import { connect } from 'react-redux';
import { addComment, likeComment } from '../redux/ActionCreators';
const mapStateToProps = state => {
return {
comments: state.comments,
likes: state.likes
}
};
const mapDispatchToProps = {
addComment: (commentId, author, text, likes, date) => (addComment(commentId, author, text,
likes, date)),
likeComment: (likes) => (likeComment(likes))
}
class Main extends Component{
render(){
return(
<div className="comment-box">
<h1>Join the discussion!</h1>
<h1>Adam's post:</h1>
<TopComment/>
<DisplayComment
addComment = {this.props.addComment}
likeComment ={this.props.likeComment}
comments = {this.props.comments}
/>
</div>
)
}
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Main));
post comment component that will add and display each comment that has been entered
through the input:
import React, { Component } from 'react';
import { Button, Card, CardBody, CardTitle, CardText } from 'reactstrap';
import { Control, LocalForm, Errors} from 'react-redux-form';
// will check validation
const required = val => val && val.length;
const maxLength = len => val => !val || (val.length <= len);
const minLength = len => val => val && (val.length >= len);
// will display comment when posted
function RenderComments({comments}) {
function handleLike(){
this.props.likeComment(this.props.likeComment)
}
if (comments) {
return (
<div className="col-sm-12">
<h4>Comments</h4>
{
comments.map(comment => {
return (
<div className="container">
<div className="row">
<div className="col-sm-12 ">
<Card style={{
backgroundColor: '#212d40',
borderColor: '#fff',
borderRadius: '0.25rem',
padding: '0.5rem 0.5rem 0.5rem',
marginBottom: '20px'
}}>
<CardBody>
<img src="./images/random.jpeg" alt="random" height="50px" />
<CardTitle tag="h3">{comment.author}</CardTitle>
<CardText>{comment.text}, <br/>
likes: {comment.likes} <br/>
{new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'short', day: '2-digit'}).format(new Date(Date.parse(comment.date)))}
</CardText> <br/>
<Button onClick={handleLike}>Like </Button>
</CardBody>
</Card>
</div>
</div>
</div>
);
})
}
</div>
);
}
return <div/>;
}
class PostComment extends Component {
handleSubmit(values) {
this.props.addComment(this.props.commentId, values.author, values.text, values.date);
}
render(){
return(
<div>
<LocalForm className="comment-form" onSubmit={ values =>
this.handleSubmit(values) }>
<div className="form-group">
<Control.text
model=".author"
className="form-control"
placeholder="name"
validators={{
required,
minLength: minLength(2),
maxLength: maxLength(15)
}}
/>
<Errors
className="text-danger"
model=".author"
show="touched"
component="div"
messages={{
required: 'Required',
minLength: 'Must be at least 2 characters',
maxLength: 'Must be 15 characters or less'
}}
/>
</div>
<div className="form-group">
<Control.text
model=".text"
className="form-control"
placeholder="comment"
/>
</div>
<Button className="btn-color" color="primary" type="submit">Submit</Button>
</LocalForm>
</div>
)
}
}
function DisplayComment(props){
return(
<div>
<div className="col">
<RenderComments
addComment = {props.addComment}
deleteComment = {props.deleteComment}
comments = {props.comments}
likeComment = {props.likeComment}
/>
</div>
<div className="col">
<PostComment
addComment = {props.addComment}
comments = {props.comments}
likeComment= {props.likeComment}
/>
</div>
</div>
)
}
export default DisplayComment
this is my commments from my shared file that has all the comment data (likes, comment, date, etc):
export const COMMENTS = [
{
commentId: 0,
author: 'Richard',
text: 'I love disum!',
likes: 1,
date: "2020-07-01T19:44Z"
},
{
commentId: 1,
author: 'Jake Paul',
text: 'Fried foods are the best!',
likes: 0,
date: "2020-09-20T19:44Z"
}
]
You're mixing the source of truth for comment likes. Should the likes count be part of the comment? Or should it be it's own thing?
Let's KISS and just keep the likes count as part of the comment as you have already set up - get rid of your other counter reducer altogether.
Your comments reducer should now look like this:
switch(action.type) {
case ActionTypes.ADD_COMMENT:
// your existing code to handle adding a comment to your state
// this new case increments the likes for a given comment
case ActionTypes.LIKE_COMMENT:
return state.comments.map(c => c.commentId === action.payload.commentId ? ({ ...c,likes: c.likes+1 }) : c);
default:
return state;
}
};

How to render an asynchronous result (array) in a component in react?

I have been doing js for about a month now, and I am writing this program where I am using clarifai API to see which celebrity a person on the photo resembles the most.
I want to pass the output as props to Rank component to render it, but
I get the
Type error: clarifaiResults.map is not a function at App.transformResponse
Basically, the response I want to pass as props is the
const clarifaiResults = response.outputs[0].data.regions[0].data.concepts[0].name;
part that I get in console.log now
I am assuming it's because there is no output yet when the app tries to render the component, but I can't figure out what's wrong with the code. Thank you!
App.js
import React, { Component } from 'react';
import './App.css';
import SignIn from './Component/SignIn/SignIn.js';
import Register from './Component/Register/Register.js';
import Particles from 'react-particles-js';
import Logo from './Component/Logo/Logo.js';
import Navigation from './Component/Navi/Navigation.js';
import ImageLinkForm from './Component/Form/ImageLinkForm.js';
import Rank from './Component/Rank/Rank.js'
import Clarifai from 'clarifai';
import FaceRecognition from './Component/Face/FaceRecognition.js';
import FaceComparison from './Component/Comparison/FaceComparison.js';
const app = new Clarifai.App({
apiKey: 'MYSUPERSECRETKEY'
});
const initialState = {
input: "",
imageUrl: "",
results: [],
route: "SignIn",
user: {
id: "",
name: "",
email: "",
entries: 0,
joined: "",
},
};
const particleOptions = {
particles: {
number: {
value: 40,
density: {
enable: true,
value_area: 800,
},
}
}
}
class App extends Component{
constructor() {
super();
this.state = initialState;
}
transformResponse = (response) => {
const clarifaiResults = response.outputs[0].data.regions[0].data.concepts[0].name;
const results = clarifaiResults.map((ingredient) => ({
ingredients: ingredient.name,
probablitiy: ingredient.value,
}));
this.setState({results: results.celebrityName});
return {results: []};
};
onInputChange = (event) => {
this.setState({input: event.target.value});
}
onSubmit = () => {
this.setState({imageUrl: this.state.input});
app.models
.predict(
Clarifai.CELEBRITY_MODEL,
this.state.input)
.then(response => {
console.log(response.outputs[0].data.regions[0].data.concepts[0].name)
if (response) {
fetch ('http://loclhost:3000', {
method: 'post',
headers: {'Conent-Type' : 'application/json'},
body: JSON.stringify({
input: this.state.user.input
})
})
.then((response) => response.json())
.then(count => {
this.setState(Object.assign(this.state.user, {entries:count}))
})
}
this.transformResponse(response);
})
.catch(err => console.log(err));
};
;
onRouteChange = (route) => {
if (route === 'signout'){
this.setState({isSignedIn: false})
} else if (route ==='home'){
this.setState({isSignedIn: true})
}
this.setState({route: route});
}
render() {
let { isSignedIn, imageUrl, route, results} = this.state;
return (
<div className="App">
<Particles className='particles'
params={particleOptions}
/>
<Navigation isSignedIn={isSignedIn} onRouteChange={this.onRouteChange}/>
{ route ==='home'
? <div>
<Logo />
<Rank
results = {results}/>
<ImageLinkForm
onInputChange={this.onInputChange}
onSubmit={this.onSubmit}
/>
<FaceRecognition
imageUrl={imageUrl}
/>
<FaceComparison
results = {results}
/>
</div>
: (
route === 'SignIn'
? <SignIn onRouteChange={this.onRouteChange}/>
: <Register />
)
}
</div>
);
};
}
export default App;
Rank.js
import React from 'react';
const Rank = ({results}) => {
const prediction = results.map((result) => {
const {ingredients} = result;
return (
<div>
<li className="celebrityName">{ingredients}</li>
</div>
);
});
if (prediction && prediction.length>1) {
return (
<div>
<div className='white f3'>
You look a lot like...
</div>
<div className='white f1'>
{results}
</div>
</div>
);
} else {
return (
<div>
</div>
)
}
};
export default Rank;

Im am getting undefined with mapstatetoprops

I am trying pass logged data from my redux actions to the front end but keep getting user.name of null or undefined.
This is the front end where I am simply trying to get user.name to appear so that it says Hi user.name(name of persons account).
import React, { Component } from "react";
import { NewPropertyForm, FormPageOne, FormPageTwo, FormPageThree,
FormPageFour } from "../../components/NewPropertyForm";
import { PropertyList } from "../../components/PropertyList";
import { Container, Button, Modal, ModalCard, ModalCardTitle,
ModalBackground, ModalCardFooter, ModalCardHeader, Delete, ModalCardBody
} from 'bloomer';
import StepZilla from "react-stepzilla";
import modal from "./modal-bg.svg";
import "./Manager.css";
import {login} from '../../actions/authActions'
import {connect} from 'react-redux';
import { bindActionCreators } from 'redux'
const steps =
[
{name: 'Step 1', component: <FormPageOne /> },
{name: 'Step 2', component: <FormPageTwo /> },
{name: 'Step 3', component: <FormPageThree /> },
{name: 'Step 4', component: <FormPageFour /> }
]
const modalBG = { backgroundImage: `url(${modal})` }
export class Manager extends Component {
// Setting our component's initial state
state = {
modal: "",
};
modalOpen = () => {
this.setState({ modal: "is-active" })
}
modalClose = () => {
this.setState({
modal: "",
login: "",
})
}
render() {
let { user } = this.props;
return (
<div className="manager">
<Container className="manager-container">
<div className="columns">
<div className="column">
<h1 className="title">Hi {user.name}</h1>
<h2 className="sub-title">You currently have 3 properties</h2>
<h2 className="sub-title">Check out the new applications you
received.</h2>
</div>
<div className="column user-dash-right">
<Button
isColor='primary'
className=""
onClick={this.modalOpen}><p>Create Listing</p></Button>
</div>
</div>
<h1 className="title has-text-centered">My Properties</h1>
<PropertyList />
<div className="new-property-modal">
<Modal className={this.state.modal}>
<ModalBackground />
<ModalCard style={ modalBG } >
<ModalCardBody>
<Delete onClick={this.modalClose} />
<div className='step-progress'>
<StepZilla
steps={steps}
showSteps={false}
nextButtonCls="button is-medium is-primary"
backButtonCls="button is-medium is-primary"
/>
</div>
</ModalCardBody>
</ModalCard>
</Modal>
</div>
</Container>
</div>
);
}
}
const mapStateToProps = ({auth}) => ({
user: auth.user,
authError: auth.authError
});
export default connect(mapStateToProps)(Manager)
This is the actions I have setup
import API from "../utils/API";
import { IS_AUTHENTICATED, AUTHENTICATION_FAILED } from
'../constants/actionTypes';
export const signup = ({name, email, phonenumber, password, role}) =>
async dispatch => {
try {
const {data} = await API.saveUser({
name,
email,
phonenumber,
password,
role
})
dispatch({
type: IS_AUTHENTICATED,
payload: data.user
})
console.log('--success', data);
} catch(error) {
console.error(error);
console.log('Come on work damnit')
}
}
export const login = ({email, password}) => async dispatch => {
try {
const {data} = await API.loginUser({
email,
password
})
dispatch({
type: IS_AUTHENTICATED,
payload: data.user
});
console.log('--success', data.user.name);
} catch(error) {
dispatch({
type: AUTHENTICATION_FAILED,
payload: "Invalid credentials, cannot login"
});
console.error(error);
}
}
export const getAuthenticated = () => async dispatch => {
try {
const {data, error} = await API.getAuthenticated();
console.log(data);
if(data) {
dispatch({
type: IS_AUTHENTICATED,
payload: data
});
} else {
console.log('ssss', error)
}
// if(getUser) login
//else logout
} catch(error) {
//window redirect to login
}
}
export const logout = () => async dispatch => {
try {
// const revoke = await API.logout()
dispatch({
type: IS_AUTHENTICATED,
payload: null
});
//should automatically display logout nav
//or redirect to anther page
} catch(e) {
//just refresh page
}
}
and these are my reducers
import {
IS_AUTHENTICATED,
AUTHENTICATION_FAILED
} from '../constants/actionTypes';
const initialState = {
user: null
}
const authReducer = (state = initialState, {type, payload}) => {
switch(type) {
case IS_AUTHENTICATED:
return {...state, user: payload, userInfo: payload}
case AUTHENTICATION_FAILED:
return {...state, user: null, authError: payload}
default:
return state
}
}
export default authReducer;
As you can see I tried to pass user.name but i keep getting cannot read property of null if I do const { user } = this.props
and i get cannot read property of undefined if i do const { user } = this.state.
I figured it out i justed needed to add
<span>
<h1 className="title">Hi {user.name}</h1>
</span>
and it worked!

Categories