Skip to main content

Command Palette

Search for a command to run...

A Pokémon themed Note Keeping Application using React!

Fun filled application for practising front end skills!

Published
19 min read
A Pokémon themed Note Keeping Application using React!
S

I am a student of University of Engineering and Management Kolkata. Currently into my junior(3rd year) of my engineering course.

Well! On writing my first technical blog, I am really feeling nostalgic about my childhood. The reason is simple. We all were at some point of our life obsessed with Pokemon! In my childhood, I and my friends used to coolly buy some chips and along with it came our little world of joy! Yeah you guessed it right!

Pokémon themed merchandise were a craze, but at the same time, we used to be mad over buying Pokémon coins - more precisely TAZOS!

Pokemon_Tazos.png

Now, I would like to suggest that you code along with me because it is best to type in the code yourself than to copy it because it builds up that muscle memory. When you're ready, let's get started - future React Master! 😉

All explanations for the project are in the code itself via the comments, but if you get stuck or want to view my CSS file, check it out on my GitHub repository:

My Git Repo

Want to test it before you make it? You can do that over here:

Project Demo

Pre Setup - Installing Packages

To complete this project exactly as (or however you want) I did, you will need to do the following in your preferred command line:

npx create-react-app note-keeper
npm i react-bootstrap bootstrap moment react-calander react-newline-to-break react-router-dom --save

The packages (apart from bootstrap) that we just installed will help us do the following:

  • react-newline-to-break: Converts your strings with newlines ("\n") to error/warning-free React components.

  • moment: A JavaScript date library for parsing, validating, manipulating, and formatting dates.

  • react-calander: Ultimate calendar for your React app.

  • react-router-dom: DOM bindings for React Router.

Step 1 - Intial setup

Set up your Index.js to contain the bootstrap modules that we will need for this project. Also, head over to FontAwesome and add your CND to your index.html header so that we can use the icons later on.

For beginners: In my git repo, just navigate to my public folder and you can easily access the index.html file mentioned here!

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
<React.StrictMode>
    <App />
</React.StrictMode>,
document.getElementById('root')
);
reportWebVitals();

This being said, you can edit your App.js file as follows and create a the following folders and files in your /src folder:

Tips: Always try to build your components inside a different folder which can be christened as components and then build your components. This avoids the clutter in your workspace and also you can breathe more freely without getting irritated XD!

components (folder)

  • Calander.js (components file)
  • EditNotes.js (components file)
  • Favorites.js (components file)
  • ListNotes.js (components file)
  • NewNotes.js (components file)
  • Notes.js (components file)
  • ToDo.js (components file)

layout (folder)

  • Header.js (layout file)
  • Clips.js (layout file)
//App.js
import React from 'react';
//our component files
import NotesApp from './components/Notes';
import Main from './components/ToDo';
import CalanderApp from './components/Calander';
import FavoriteLinks from './components/Favorites';
import Header from './layout/Header';
import Clip from './layout/Clips';
//Our Router components for DOM navigation
import { BrowserRouter, Route } from 'react-router-dom';
//Our React-Bootstrap Components
import {Container, Col, Row} from 'react-bootstrap';

//main App component
function App() {
  return (
    <div className="App">
      <Container>
        <Row>
        <header className="App-header">
              //will show our clip image (optional add-in)
              <Clip /> 
          </header>
          <header className="App-header">
              //will show our page title
              <Header /> 
          </header>
        </Row>
        <Row>
            <Col className="col-12 col-md-6 col-lg-6"> 
              //will show our notes section
              <BrowserRouter>
                <Route path="/" component={NotesApp}/> 
              </BrowserRouter>
            </Col>
            <Col className="col-12 col-md-6 col-lg-6">  
              //will show our to-do section              
              <BrowserRouter>
                <Route path="/" component={Main}/> 
              </BrowserRouter>
            </Col>
        </Row>
        <Row>
          <Col className="col-12 col-md-6 col-lg-6"> 
            //will show our favorites section
            <BrowserRouter>
              <Route path="/" component={FavoriteLinks}/> 
            </BrowserRouter>
          </Col>
          <Col className="col-12 col-md-6 col-lg-6"> 
            //will show our calender section
            <BrowserRouter>
                <CalendarApp />  
            </BrowserRouter>
          </Col>
        </Row>
      </Container>
    </div>
  );
}

//exports the App component to be used in index.js
export default App;

Step 2 - Layout files

For this section, we will create our least-important files for the project just to get it out of the way. These are just UI components, so it's best to do it first (at least for me) so that we can focus on the functionality of our project more. Don't forget to copy my css code and the custom fonts needed for the project from my GitHub so that you don't get errors upon compilation (otherwise just add your own styling or remove it)! So, to the Header.js file, make the following changes:

//Header.js 
import React from 'react';
import {Container, Row} from 'react-bootstrap';

function Header() {
    return (
        <Container>
            <Row>
                <div className="Header">
                    <h1 className="app-title">
                        //copy the .pixels CSS from my github
                        <span className='pixels'> pxn </span>
                        Poket Book
                        <span className='pixels'> cli </span>
                    </h1>           
                </div>
            </Row>
        </Container>
    );
  }

//Exports Header Component to be used in app.js
export default Header;

And then make the following changes to your Clips.js and save:

//Clips.js (optional)
import React from 'react';
import {Container, Row} from 'react-bootstrap';

function Clips() {
    return (
        <Container>
            <Row>
                <div className="clip">
                    <div className="clip">
                        <img src="css/images/clip.png" alt=""></img>
                    </div>      
                </div>
            </Row>
        </Container>
    );
  }

//Exports Header Component to be used in app.js
export default Clips;

Step 3 - Notes section

To get started with our notes section, we will need to first make the following changes to our ListNotes.js so that we can list the notes that we will display on our main Notes.js component as we go.

//ListNotes.js 
import React, {Component} from 'react';
import {NavLink} from 'react-router-dom';
import moment from 'moment';
//moment is a JavaScript date library for parsing, validating, manipulating, and formatting dates. 

//we use a class component because our notes will consits of states and inheritance from a parent component which will pass properties down the functional component via props.
class ListNotes extends Component {
    //will render date to be displayed of note that was last added/edited
    renderFormattedDate(date){
        return moment(date).format('DD MMM YYYY');
    }
    render() {
        //if there are no notes to list, we will display a div with a message
        if (!this.props.notes || this.props.notes.length === 0) {
            return (<div className="no-notes">Oops! It seems that you have no notes. Try adding one? 😊</div>)
        }
        //if there are notes to list, we will display a div with the notes
        const listItems = this.props.notes.map((note) =>
                //nav link to the div of respective note without displaying the id
                <NavLink activeClassName='active' to={`/note/${note.id}`}
                      className="list-group-item"
                      key={note.id.toString()}
                      onClick={this.props.viewNote.bind(this, note.id)}>
                    {/*Show note title*/}
                    <div className="text-truncate primary">{note.title}</div>
                    {/*Show note date*/}
                    <div className="font-weight-light font-italic small">{this.renderFormattedDate(note.date)}</div>
                </NavLink >
        );
        //Displays the notes as a list
        return (<ul className="list-group">{listItems}</ul>);
    }
}

//exports for use in other files
export default ListNotes;

Then, to be able to add new notes to our Notes.js component, we will need to do the following:

//NewNotes.js
import React from 'react';
import { Redirect } from 'react-router';
//React Router is a collection of navigational components. 

//this will hide our note id div from showing on the note screen
const divStyle = {
    display: 'none'
};

//we use a class component because our notes will consits of states and inheritance from a parent component which will pass properties down the functional component via props.
class NewNotes extends React.Component {

        //We use a constructor to set the initial state of the class object
        constructor(props) {
        super(props);
        //we set the initial state of the note nav to false, ie. there will be no notes to show thus no notes to "redirect" to when clicked
        this.state = {
            redirect: false
        };
        //we bind the components to our event handlers to be executed
        this.saveNote = this.saveNote.bind(this);
        this.deleteNote = this.deleteNote.bind(this);
    }

    //saveNote Event Handler which will save a new note
    saveNote(event) {
        //the preventDefault() option is added to stop the page from reloading upon submitting a note
        event.preventDefault();
        //if the Title of the note is empty, we validate it via alert
        if (this.title.value === "") {
            alert("Title is needed");
        } else {
             //we assign each note with an id, title, desc and image upon submit
            const note = {
                id: Number(this.id.value),
                title: this.title.value,
                description: this.description.value
            }
             //we set the new state of the note nav to true so that it can "redirect" to the note when clicked
            this.props.persistNote(note);
            this.setState({
                redirect: true
            });
        }
    }

    //deleteNote Event Handler which will delete(cancel the addition) a new note
    deleteNote(event) {
        //testing purposes only
        console.log('deleteNote');
        //the preventDefault() option is added to stop the page from reloading upon submitting a note
        event.preventDefault();
        //we remove the note by deleting the respective id (note key)
        this.props.deleteNote(this.props.note.id);
    }

    //Switch between and then render(show) note titles, ie. either add a note or edit an exisitng note title.
    renderFormTitleAction() {
        return (this.props.note.id !== undefined) ? "Edit Note" : "New Note";
    }

    //Render(show) save/delete note buttons for a new or existing note.
    renderFormButtons() {
        //if the note.id exists, then we can either delete or edit that note
        if (this.props.note.id !== undefined) {
            return (<div>
                { /* Show the save button to edit note */}
                <button type="submit" className="btn btn-success float-right">Add Note</button>
                { /* Show the delete button to delete note */}
                <button onClick={this.deleteNote} className="btn btn-danger">Delete Note</button>
            </div>);
        }
        return (
            /* Show the add button to save a new note */
            <button type="submit" className="btn btn-success float-right">Add Note</button>
        );
    }

    render() {

        //existing note redirection
        if (this.state.redirect) {
            //if the note doesn't exist, we return to main "/"
            if (!this.props.note) {
                return <Redirect push to="/"/>;
            }
            //route to an existing note upon redirect, ie. note id: 1 will redirect to http://localhost:3000/note/1
            return <Redirect push to={`/note/${this.props.note.id}`}/>;
        }

        return (
            <div className="card">
                <div className="card-header">
                    {/* This will render the correct titles depending on if there are existing notes or not*/}
                    {this.renderFormTitleAction()}
                </div>
                <div className="card-body">
                    {/* Form that allows us to add a new note*/}
                    <form ref="NewNotes" onSubmit={this.saveNote}>
                        <div className="form-group">
                            {/* Renders a new note id (divStyle will hide this from view)*/}
                            <p className="note_id">
                                <input className="form-control" style={divStyle} disabled ref={id => this.id = id} defaultValue={this.props.note.id}/>
                            </p>
                            {/* Renders a new note title */}
                            <p className="note_title">
                                <label className="noteTitle">Title</label>
                                <input className="form-control" ref={title => this.title = title} defaultValue={this.props.note.title} placeholder="Save Princess Peach"/>
                            </p>
                            {/* Renders a new note description*/}
                            <p className="note_desc">
                                <label className="noteDescTitle">Description</label>
                                <textarea className="form-control" rows="10" ref={description => this.description = description} defaultValue={this.props.note.description} placeholder="When Mario reaches the end of the course, remember to save Princess Peach or Luigi will! "/>
                            </p>
                        </div>
                        {/* This will render the correct buttons depending on if there are existing notes or not*/}
                        {this.renderFormButtons()}
                    </form>
                </div>
            </div>
        )
    }
}

//exports for use in other files
export default NewNotes;

Next up is the option to edit pre-added or new notes on our main Notes.js component.

//EditNotes.js
import React from 'react';
import { Redirect } from 'react-router';
import moment from 'moment';
import newline from 'react-newline-to-break';
//moment is a JavaScript date library for parsing, validating, manipulating, and formatting dates. 

//class component will switch between editing and deleting note rendering states
class EditNotes extends React.Component {
    //We use a constructor to set the initial state of the class object
        constructor(props) {
        super(props);
        //we set the initial state of the note nav to false, ie. there will be no notes to show thus no notes to "redirect" to when clicked
        this.state = { 
            redirect : false
        };
        //we bind the components to our event handlers to be executed
        this.deleteNote = this.deleteNote.bind(this);
        this.editNote = this.editNote.bind(this);
    }

    //deleteNote Event Handler which will delete an existing note
    deleteNote(event){
        //the preventDefault() option is added to stop the page from reloading upon submitting a note
        event.preventDefault();
        //we remove the note by deleting the respective id (note key)
        this.props.deleteNote(this.props.note.id);
    }

    //editNote Event Handler which will update an existing note
    editNote(event){
        //the preventDefault() option is added to stop the page from reloading upon submitting a note
        event.preventDefault();
        //we edit the note by updating the respective id (note key)
        this.props.editNote(this.props.note.id);
    }

    //will render to be displayed when a new date whenever a note is edited
    renderFormattedDate(){
        return 'Last edited:' + moment(this.props.note.date).format("DD MMM YYYY [at] HH:mm");
    }

    render() {
        //if the note doesn't exist, we return to main "/"
        if (this.state.redirect || !this.props.note) {
            return <Redirect push to="/"/>;
        }

        //else we return a card with the note details
        return (
            <div className="card">
                {/*Will render the note title*/}
                <div className="card-header">
                    <h4>{this.props.note.title}</h4>
                </div>
                <div className="card-body">
                    {/*Will render the note added/last updated date*/}
                    <p className="text-center font-weight-light small text-muted">{this.renderFormattedDate()}</p>
                    {/*Will render the note description*/}
                    <p className="card-text-main">Title: {newline(this.props.note.title)}</p>
                    <p className="card-text">{newline(this.props.note.description)}</p>
                    {/*Will render the delete button*/}
                    <button onClick={this.deleteNote} className="btn btn-danger">Delete</button>
                    {/*Will render the edit button*/}
                    <button onClick={this.editNote} className="btn btn-success float-right">Edit</button>
                </div>
            </div>
        )
    }
}

//exports it for use in other files
export default EditNotes;

Now that we have created our components that will allow us to Add, Edit and List our notes, we can update our main Notes.js component as follows. While we're at it, we will also add the functionality to view and delete notes according to their note id.

//Notes.js
import React from 'react';
import moment from 'moment';
import NewNotes from './NewNotes';
import EditNotes from './EditNotes';
import NotesList from './ListNotes';
import { Route, Link } from 'react-router-dom';

//class component will switch between displaying all existing or new note rendering states
class NotesApp extends React.Component {
        //We use a constructor to set the initial state of the class object
        constructor(props) {
        super(props);
        //will store the notes on our localStorage for storing user notes (local testing purposes)
        const notes = localStorage.getItem('notes') ? JSON.parse(localStorage.getItem('notes')) : [];
        //sets the initial state of all notes on storage base
        this.state = {
            notes: notes,
            selectedNote: null,
            editMode: false
        };
        //we bind the components to our event handlers to be executed
        this.getNotesNextId = this.getNotesNextId.bind(this);
        this.addNote = this.addNote.bind(this);
        this.viewNote = this.viewNote.bind(this);
        this.openEditNote = this.openEditNote.bind(this);
        this.saveEditedNote = this.saveEditedNote.bind(this);
        this.deleteNote = this.deleteNote.bind(this);
    }

    //Initiates the note id's that are/will be stored via the localStorage 
    getNotesNextId() {
        return this.state.notes.length > 0 ? this.state.notes[this.state.notes.length - 1].id + 1 : 0;
    }

    //we persist the fetched data as string because we get the stored value parsed as a boolean, ie. does it have notes (yes/no)
    persistNotes(notes) {
        localStorage.setItem('notes', JSON.stringify(notes));
        this.setState({notes: notes});
    }

    //we give each note an id, date and new persisted state when we add a new note and push it to the notes local array.
    addNote(note) {
        //set notes values
        note.id = this.getNotesNextId();
        note.date = moment();
        const notes = this.state.notes;
        //adds new note values
        notes.push(note);
        this.persistNotes(notes);
        this.setState({selectedNote: null, editMode: false});
    }

    //we view each note via mapping over it's id array, and when it is not found we handle it via an arror handler
    viewNote(id) {
        const notePosition = this.state.notes.findIndex((n) => n.id === id);
        //display the note on the screen
        if (notePosition >= 0) {
            this.setState({
                selectedNote: this.state.notes[notePosition], 
                editMode: false
            });
        } 
        //error handler
        else {
            console.warn('The note with the id ' + id + ' was not found. Please try again.');
        }
    }

    //we edit each note via mapping over it's id array, and when it is not found we handle it via an arror handler
    openEditNote(id) {
        const notePosition = this.state.notes.findIndex((n) => n.id === id);
        //displays the note to edit on screen
        if (notePosition >= 0) {
            this.setState({
                selectedNote: this.state.notes[notePosition], 
                editMode: true
            });
        } 
        //error handler
        else {
            console.warn('The note with the id ' + id + ' was not found. Please try again.');
        }
    }

    //we save each note via mapping over it's id array, and when it is not found we handle it via an arror handler
    saveEditedNote(note) {
        const notes = this.state.notes;
        const notePosition = notes.findIndex((n)=> n.id === note.id);
        //displays the note to add on screen
        if (notePosition >= 0) {
            note.date = moment();
            notes[notePosition] = note;
            this.persistNotes(notes);
        } 
        //error handler
        else {
            console.warn('The note with the id ' + note.id + ' was not found. Please try again.');
        }
        //updates notes to list
        this.setState({
            selectedNote: note, 
            editMode: false
        });
    }

    //we delete each note via mapping over it's id array, and when it is not found we handle it via an arror handler
    deleteNote(id) {
        const notes = this.state.notes;
        const notePosition = notes.findIndex((n)=> n.id === id);
        //deletes the note from the screen screen
        if (notePosition >= 0) {
            if (window.confirm('Are you sure you want to delete this note?')) {
                notes.splice(notePosition, 1);
                this.persistNotes(notes);
                this.setState({selectedNote: null, editMode: false});
            }
        } 
        //error handler
        else {
            console.warn('The note with the id ' + id + ' was not found. Please try again.');
        }
    }

    //initiates the values of each new note
    getEmptyNote() {
        return {
            title: "",
            description: "",
            image: ""
        };
    }

    //renders the notes list menu on the screen
    renderMenu () {
        return (
            <div className="card">
                {this.renderHeader()}
                <div className="card-body">
                    <NotesList notes={this.state.notes} viewNote={this.viewNote}/>   
                </div>
            </div>
        )
    }

    //renders the notes header on the screen
    renderHeader() {
        return (
            <div className="card-header">
                {/*renders close view*/ }
                <Route exact path="/note" render={routeProps => 
                    <Link to="/">
                        <button type="button" className="btn btn-danger">Cancel Note</button>
                    </Link> }/>
                {/*renders note view*/ }
                {["/", "/note/:id"].map(path =>
                <Route key={path} exact path={path} render={routeProps => 
                    <Link to="/note">
                        <button type="button" className="btn btn-success">New Note</button>
                    </Link>}/>
                )}
            </div>
        )
    }

    //display the notes when clicked on for editing, note and empty note views
    setMainAreaRoutes() {
        const editMode = this.state.editMode;
        return (<div>
            {/*edits either the new note or exisitn note*/ }
            {editMode ? (
                <Route exact path="/note/:id"
                       render={routeProps => <NewNotes persistNote={this.saveEditedNote} deleteNote={this.deleteNote} note={this.state.selectedNote}/>}
                    />
                ) : (
                <Route exact path="/note/:id" render={routeProps =>     
                    <EditNotes editNote={this.openEditNote} deleteNote={this.deleteNote} note={this.state.selectedNote}/>}
                />
            )}
            {/*displays if no notes can be found*/ }
            <Route exact path="/note"
                   render={routeProps =>  <NewNotes persistNote={this.addNote} note={this.getEmptyNote()}/>}
                />
        </div>)
    }

    render() {
        return (
            <div className="notesApp container-fluid">
                 <div className="card-notes-header">
                    <h2> NOTES </h2>
                </div>
                <div className="row">
                    {/*renders note list menu*/ }
                    <div className="col-12">
                        {this.renderMenu()}  
                    </div>
                    {/*renders note area menu*/ }
                    <div className="col-12">
                        {this.setMainAreaRoutes()}
                    </div>
                    </div>
            </div>
        );
    }
}

//exports for use in other files
export default NotesApp;

After completing all the steps above, recheck your work by comparing all your work with mine.

If you have done all those successfully, then something should come up on your screen like this:

Notes_Part.png

Step 4 - To Do section

Now that we've added our notes section, it is time to move on to our To-Do list. Now, in this component, we will be able to add new to-do-list items, mark them as complete, unmark them, and even delete them.

As a small challenge, you can update this to also work with Local Storage as we did in the Notes.js section!

//ToDo.js
import React from 'react';
import {Col, Row} from 'react-bootstrap';

//Initiate the ToDo function that will display our main display components, ie the list, check/uncheck button, and delete button
function Todo({ todo, index, completeTodo, unCompleteTodo, removeTodo }) {
    return (
          <div
            className="todo"
            style={{ textDecoration: todo.isCompleted ? "line-through" : "" }}
          >
          {todo.text}
          <div>
          {/*completes list*/}
          <button onClick={() => completeTodo(index)} className="btn btn-icon-check"> 
            <i className="fas fa-check-circle"></i>
          </button>
          {/*uncompletes list*/}
          <button onClick={() => unCompleteTodo(index)} className="btn btn-icon-redo"> 
          <i className="fas fa-redo"></i>
          </button>
          {/*deletes list*/}
          <button onClick={() => removeTodo(index)} className="btn btn-icon-trash"> <i className="fas fa-trash"></i> </button>
        </div>
      </div>
    );
  }

  //sets our initial state of our todo list to null
  function TodoForm({ addTodo }) {
    const [value, setValue] = React.useState("");
    const handleSubmit = e => {
      e.preventDefault();
      if (!value) return;
      addTodo(value);
      setValue("");
    };

    //returns a form to add a new todo item to our list
    return (
      <form onSubmit={handleSubmit} className="card-header-todo mb-3">
        <Row>
          <Col className="col-md-8">
            <input 
            type="text"
            className="input"
            value={value}
            onChange={e => setValue(e.target.value)
            }/>
           </Col>
           <Col className="col-md-4 btn-add">
            <button type="submit" className="btn-success">Add To-Do</button>
           </Col>
        </Row>
      </form>
    );
  }

  //Main function ties it together
  function Main() {
    //default values are passed for display purposes
    const [todos, setTodos] = React.useState([
      {
        text: "Do Some Magic With React 🔮",
        isCompleted: false
      },
      {
        text: "Ban Townies From Sims Game ❌",
        isCompleted: false
      },
      {
        text: "Water The Dead Cactus 🌵",
        isCompleted: false
      }
    ]);

    //adds a todo to the list
    const addTodo = text => {
      const newTodos = [...todos, { text }];
      setTodos(newTodos);
    };

    //checks the complete button and strikes through the text
    const completeTodo = index => {
      const newTodos = [...todos];
      newTodos[index].isCompleted = true;
      setTodos(newTodos);
    };

    //checks the uncomplete button and unstrikes through the text
    const unCompleteTodo = index => {
      const newTodos = [...todos];
      newTodos[index].isCompleted = false;
      setTodos(newTodos);
    };

    //deletes the whole list item as a whole
    const removeTodo = index => {
      const newTodos = [...todos];
      newTodos.splice(index, 1);
      setTodos(newTodos);
    };

    //renders the main ui of to do list
    return (
      <div className="todoList container-fluid">
        <div className="todo-header">
          <div className="todo-list-header">
              <h2>TO-DO </h2>
          </div>  
      </div>   
          <div className="card">
              <div className="card-body todo-body">
                  {/*form to add a new to do item*/}
                  <div className="card-todo-form">
                      <TodoForm addTodo={addTodo}/>
                  </div>
                  <div className="card-list">
                      {/*maps over todo items and instantiates functions for existing items*/}
                      {todos.map((todo, index) => (
                      <Todo
                        key={index}
                        index={index}
                        todo={todo}
                        completeTodo={completeTodo}
                        removeTodo={removeTodo}
                        unCompleteTodo={unCompleteTodo}
                      />
                    ))}   
                  </div>
              </div><div className="card-pixels-todo">
                    <span className="pixels">todos</span>
                    </div>
            </div>
      </div>
    );
  }

  //exports for use in other files
  export default Main;

You should get something like this before:

Todo_Part.png

#Step 5 - Favorites section

Our favorites section functions extremely similar to our ToDo.js file, it just has the added extra of visiting our favorites. As an extra bonus challenge, you can update this to also work with Local Storage as we did in the Notes.js section!

//Favorites.js
import React from 'react';
import {Col, Row} from 'react-bootstrap';

//Initiate the Faves function that will display our main display components, ie the link, button, and category
function Faves ({ favorite, visitFaves, index, removeFaves }) {
    return (
        <Row className="fave-link">
            {/*displays link*/}
            <Col className="col-8 favorites-p"> 
                <a href={favorite.text}>{favorite.text}</a>
            </Col>

            {/*deletes favorite*/}
            <Col className="col-4"> 
                <button onClick={() => removeFaves(index)} className="btn btn-icon-trash"> <i className="fas fa-trash"></i> </button>

                <button onClick={() => visitFaves(index)} className="btn btn-icon-redo"><i className="fas fa-globe"></i> </button>
            </Col>
        </Row>
    );
  }

    //sets our initial state of our fave list to null
    function FaveForm({ addFaves }) {
    const [value, setValue] = React.useState("");
    const handleSubmit = e => {
      e.preventDefault();
      if (!value) return;
      addFaves(value);
      setValue("");
    };

    //returns a form to add a new fave item to our list
    return (
      <form onSubmit={handleSubmit} className="mb-3">
        <Row>
          <Col className="col-md-8 ">
            <input 
            type="text"
            className="faves-input"
            value={value}
            onChange={e => setValue(e.target.value)
            }/>
          </Col>
          <Col className="col-md-4">
            <button type="submit" className="faves-input-btn">Favorite!💖</button>
          </Col>
        </Row>
      </form>
    );
  }

    //FavoriteLinks function ties it together
    function FavoriteLinks() {
    const [favorites, setFaves] = React.useState([
    //default values are passed for display purposes
      {
        text: "https://www.youtube.com"
      },
      {
        text: "https://github.com/christinec-dev"
      },
      {
        text: "https://developer.mozilla.org/"
      }
    ]);

    //adds a favorite to the list
    const addFaves = text => {
      const newFaves = [...favorites, { text}];
      setFaves(newFaves);
    };

    //deletes the favorite from list
    const removeFaves = index => {
      const newFaves = [...favorites];
      newFaves.splice(index, 1);
      setFaves(newFaves);
    };

    //deletes the favorite from list
    const visitFaves = index => {
      const newFaves = window.location.href=`{favorite.text}`;
      setFaves(newFaves);
    };

    //renders the main ui of to do list
    return (
      <div className="favorites mb-3 container-fluid">
         <div className="favorites-header">
            <h2>FAVORITE SITES</h2>
         </div>
      <div className="card">
        <div className="card-body favorites">
        <Row>
            <Col className="col-md-8">
                <h3 className="cat-header">Website</h3> 
            </Col>
            <Col className="col-md-4">
                <h3 className="cat-header">Modify</h3>
            </Col>
        </Row>
        {/*maps over todo items and instantiates functions for existing items*/}
          {favorites.map((favorite, index, category) => (
            <Faves
              key={index}
              index={index}
              favorite={favorite}
              removeFaves={removeFaves}
              visitFaves={visitFaves}
              category={category}  
            />
          ))}
        {/*form to add a new item*/}
          <div className="faves-form">
            <FaveForm addFaves={addFaves}/>
          </div>
        </div>
      </div>
      </div>
    );
  }

//exports for use in other files
export default FavoriteLinks;

You should get something like this

Favorites_Part.png

Step 6 - Calendar Section

We're almost at the end, and what better way to wrap up our notes keeping app than to add a calander? Now for this, we use the calander npm package installed above to render our calander. It is a neat package because you can view the weeks, months, year(s), and even the decades without having to code anything!

//Calender.js
import React, {useState} from 'react'
import Calendar from 'react-calendar'
import 'react-calendar/dist/Calendar.css';

//calender that will be shown on our main page
export default function CalenderApp () {
//main date functions to initialize our date state
const [dateState, setDateState] = useState(new Date())
//changes date to current selection on calender
const changeDate = (e) => {
    setDateState(e)
}
return (
    //returns the calender as rendered cal
    <div className="container-fluid">
      <div className="favorites-header">
            <h2>CALENDER</h2>
         </div>
      <div className="calender-main">
        <div className="card">
          <div className="calender-card-body">
            <>
            <Calendar 
            value={dateState}
            onChange={changeDate}
            className="calender-body"
            />
          </>
        </div>
      </div>
    </div>
  </div>
  )
}

You should get this look when you get done with this part.

Calendar_Part.png

#Section 7 - Wrapping up

Now that you have created all the components, and added the necessary CSS styling, it is time to test our application. I do this frequently during project creation to test my code, but during this tutorials we only test it at the end - however you want to do it, is up to you! Run your project with the following command:

npm start

You should get something similar to this:

Final_Website.png

Atlast after a long work we are finally here! Our short journey of me guiding you through this project stops here partially! Why partially?? Because we have not yet deployed our site!

Do you want this project to be hosted ? Want my guidance on it? Like this blog and comment below "Deployment Op" to make me release my deployment article for this one. Until then, goodbye and take care!

S

Explanation OP

1
S

Thanks!

S

I really got the pokemonistic vibe after using this web app & also it's an amazing & in-depth blog that's gonna help the community to build awesome Reactjs applications like this.

2
S

This comment made my day. Thanks a lot. Will definitely try to write more blogs in future.

S

It is very helpful for newbies like me. Keep up brother!! Am looking forward for more projects from you!!!

1
S

Will try my best. Thanks.

J

Explanation ❤️

1
S

Thanks a lot bro.

B

Very well explained!

1
S

Thanks a lot.

N

Deployment OP!

S

Thanks. XD

R

Khub sundor hoyeche....... Ekdom smooth...... Best wishes and regards....

1
S

Thanks bro!

K

Great tutorial. Explained in very layman terms. And quite a fun project too. Fun way to do such a task, which many can found boring. Quite a smooth User Experience too. Have you thought about making the font a bit bold? It'll make the texts more prominently visible.

Last but not the least: "Deployment Op" :-D

3
S

Thanks a lot! Will get motivated by this simple comment. And yeah will be working to make the texts bolder next time.

1
A

Ei ne Like and comment Op developer

2
S

Thanks man!