Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,33 @@
import './App.css';
import ChatLog from './components/ChatLog';
import data from '/src/data/messages.json';
import { useState } from 'react';


const App = () => {

const [entries, setEntries] = useState(data)

function toggleLike(id) {
const updatedEntries = entries.map(entry => {
if (entry.id === id) {
return { ...entry, liked: !entry.liked }
}
return entry;
});
setEntries(updatedEntries);
};
Comment on lines +11 to +19

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work on using .map to map over your entries! Very functional! Since you are using a simple conditional block here, we could make this more succinct by using a ternary instead like so:

const toggleLike = (id) =>
  setEntries(entries.map(entry =>
    entry.id === id ? { ...entry, liked: !entry.liked } : entry
  ));

When you find yourself with simple checks like these, more often than not a ternary could be implemented instead for conciseness but still maintaining some kind of readability.


const totalLikes = entries.filter(entry => entry.liked).length;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also do this with the reduce method like so:

const  totalLikes  =  chatMessages.reduce((count,  message)  =>  count  + (message.liked ? 1 : 0),  0);


return (
<div id="App">
<header>
<h1>Application title</h1>
<h1>Chat Between Vladimir and Estragon</h1>
<h3>{totalLikes} ❤️s</h3>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⭐️

</header>
<main>
{/* Wave 01: Render one ChatEntry component
Wave 02: Render ChatLog component */}
<ChatLog entries={entries} onToggleLike={toggleLike} />

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🫡

You may see conventions where you put the props for a component on separate lines like so:

<ChatLog 
entries={entries} 
onToggleLike={toggleLike} 
/>

This is one way to make components with many props more readable!

</main>
</div>
);
Expand Down
25 changes: 18 additions & 7 deletions src/components/ChatEntry.jsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
import './ChatEntry.css';
import TimeStamp from './TimeStamp';
import propTypes from 'prop-types';

const ChatEntry = () => {
const ChatEntry = ({ id, sender, body, timeStamp, liked, onToggleLike }) => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work de-structuring your props! ⭐️


const localOrRemote = sender === 'Vladimir' ? 'chat-entry local' : 'chat-entry remote';

return (
<div className="chat-entry local">
<h2 className="entry-name">Replace with name of sender</h2>
<div className={localOrRemote}>
Comment on lines +7 to +10

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⭐️

<h2 className="entry-name">{sender}</h2>
<section className="entry-bubble">
<p>Replace with body of ChatEntry</p>
<p className="entry-time">Replace with TimeStamp component</p>
<button className="like">🤍</button>
<p>{body}</p>
<p className="entry-time">
<TimeStamp time={timeStamp} />
Comment on lines +11 to +15

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job on interpolating your JS (props) into your JSX object return body and leveraging your TimeStamp component!

</p>
<button className="like" onClick={() => onToggleLike(id)}>{liked ? '❤️' : '🤍'}</button>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏿

</section>
</div>
);
};

ChatEntry.propTypes = {
// Fill with correct proptypes
sender: propTypes.string.isRequired,
body: propTypes.string.isRequired,
timeStamp: propTypes.string.isRequired,
liked: propTypes.bool.isRequired,
onToggleLike: propTypes.func
Comment on lines +24 to +28

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⭐️

Suggested change
sender: propTypes.string.isRequired,
body: propTypes.string.isRequired,
timeStamp: propTypes.string.isRequired,
liked: propTypes.bool.isRequired,
onToggleLike: propTypes.func
sender: propTypes.string.isRequired,
body: propTypes.string.isRequired,
timeStamp: propTypes.string.isRequired,
liked: propTypes.bool.isRequired,
onToggleLike: propTypes.func.isRequired

};

export default ChatEntry;
23 changes: 23 additions & 0 deletions src/components/ChatLog.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import './ChatLog.css';
import ChatEntry from './ChatEntry';

const ChatLog = ({ entries, onToggleLike }) => {
const chatEntries = entries.map(entry => (
<ChatEntry
key={entry.id}
id={entry.id}
sender={entry.sender}
body={entry.body}
timeStamp={entry.timeStamp}
liked={entry.liked}
onToggleLike={onToggleLike}
/>
));
Comment on lines +5 to +15

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the names of the keys on entry are the same as the names your are setting on ChatEntry attributes/you are using all of the values in entry you could do something something like this to save you a few keystrokes:

const  chatComponents  =  entries.map((entry)  =>  {
  return(
    <ChatEntry
	{...entry}
	onLikeToggle={onLikeBtnToggle}
	key={entry.id}
    />
  );
});

Though, what you have is more verbose! The suggestion I have above can be used when we want to use pass every key/value pair to the child component. If not, then we wouldn't follow the suggested pattern since it would be passing impertinent information to a component, putting it at risk to accidentally change a piece and be more bug prone.


return (
<section className='chat-log'>{chatEntries}</section>
)

};

export default ChatLog;
Comment on lines +21 to +23

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You didn’t include prop types for this component. While PropTypes is deprecated in React v19, it's still important to ensure consistent type safety across your application. Recall that React encourages you to adopt a type system like TypeScript to prevent type-related bugs and improve code reliability. @malikelmessiry