12/31/19

Building MonthPicker With React Hooks - Part 3

Reminder

Hi guys, thanks for following my blog. This post is third part of: "Building monthpicker using nothing but react hooks" series. It is better for you to have a look on previous two parts (if you didnt do it before)

Year navigation

Now for final functionalities - like navigating between years in dropdown header


   ... 
   const [year, setYear] = useState((new Date()).getFullYear());

   const incYear = e => {
      e.preventDefault();
      setYear(year + 1);
   }

   const decYear = e => {
      e.preventDefault();
      setYear(year - 1);
   }

   ...
Pressing the two ">" shaped buttons at dropdown header - will move you to prevous or next year.

One last thing - (without it the target cannot consider accomplished) is "monthClick" hadler. the month should change when some of "months" clicked. This could be easily achieved by attaching proper handler to each button:

   const monthClick = (e, idx) => {
      const currmonth = `0${idx+1}`.slice(-2);
      onChange(`${currmonth}.${year}`);
      toggleMenu();
   }
That it, you can view this codesandbox to see the whole code:

12/26/19

Building MonthPicker With React Hooks - Part 2

Outside Click Behavior

It is common feature for a menus - to be closed when user clicks somewhere else (outside the dropdown panel). To achileve this functionality - we can use this custom hook


import { useEffect } from "react";

function UseOuterClickNotifier(onOuterClick, innerRef) {
    useEffect(
      () => {
        // only add listener, if the element exists
        if (innerRef.current) {
          document.addEventListener("click", handleClick);
        }
  
        // unmount previous first in case inputs have changed
        return () => document.removeEventListener("click", handleClick);
  
        function handleClick(e) {
          innerRef.current && !innerRef.current.contains(e.target) && onOuterClick(e);
        }
      },
      [onOuterClick, innerRef] // invoke again, if inputs have changed
    );
}

export default UseOuterClickNotifier;

So now , our month menu opens when clickinh inside input and closes when clicking outside

   UseOuterClickNotifier(
      e => setShown(false),
      elRef
   );

Displaying Months

Now - to the main purpose of the our widget: the displaying months:


   const months = ['Jan', 'Feb', 'Mar', 'Spr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
   ...

   {shown && <div className="month-menu-dropdown">
     <header>
      <div className="top-arrow"></div>
      <button onClick={decYear}>></button>
      <div className="year-holder">{year}</div>
      <button onClick={incYear}><</button>
    </header>
     {months.map((month, index) => (<button key={month} onClick={e => monthClick(e, index)}>{month}</button>))}
    </div>}

to get the buttons ordered at 3 columns and 4 rows, i will use grid-css feature:

.month-menu-dropdown {
    position: absolute;
    right: 0;
    top: 59px;
    min-width: 170px;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    column-gap: 2px;
    row-gap: 5px;
    z-index: 3;
    background: #fff;
    border: 1px solid var(--text-color);
    border-radius: 2px;
    border: 1px solid grey;
    padding: 5px 0;
}

12/23/19

Building MonthPicker With React Hooks - Part 1

Motivation

I thought it can be interesting to make a post about - how i build a "month picker" util, by myself, using my little css/html/react knowledge and nothing else.
Searching through the web - i stumble upon this implementation of month picker, but after looking more deeply into the code i asked myself - is there some way to implement this functionality little more simplier-for-user?

Design

First step - lets think about how this monthpicker should look and how it should act.
Here is screencast of monthpicker in action:

Popup Menu

First thing to start with - i thought about "popup menu" - a dropdown menu which pops when user clicks the input


import React, {useState, useRef} from 'react';
const MonthPicker = () => {
   const [shown, setShown] = useState(false); 
   const elRef = useRef(null);
   const toggleMenu = () => {
      setShown(!shown);
   }

   return (
      <span className="month-menu-holder" ref={elRef}>
         
         <input onFocus={toggleMenu}/>
        
         {shown && <div className="month-menu-dropdown">
            <header>
               <div className="top-arrow"></div>
               <button><</button>
               <div className="year-holder">{'2019'}</div>
               <button>></button>
            </header>
            {/* here will come a days buttons */}
         </div>}
      </span>
   );
}

Currently - the "Thing" we created - just know how to make the drop menu to appear, but - that is a good start. Of course - most of the "tricks" of invisibility and positioning done by proper css:

.month-menu-holder {
    position: relative;
    display: inline-block;
}

.month-menu-dropdown {
    position: absolute;
    right: 0;
    top: 59px;
    min-width: 170px;
    ...
}


12/18/19

Interesting Code Adventure With Compare the Array Of Objects

Recently i had some interesting task: The form i was working on - was supposed to update array of objects:


[
 {id: 1, framework: 'React'},
 {id: 2, framework: 'Angular'},
 {id: 3, framework: 'Vue'},
]

The problem was - the api has a support only for only one object at time and according to design - a form was planned to have only one "save" button (which suppose to trigger a batch saving)

 POST - api/framework
 DELETE - api/framework/:id
 UPDATE -api/framework/:id

So i needed to find a way to compare the two arrays - the original and the modified, and store the changes - in order to send a proper request for each one.

I will post Solution i finally came with

Firstly i decided to compare the strings of strigified arrays:


    const isIdentical = JSON.stringify(modifiedArr) === JSON.stringify(oldArr);
    if (isIdentical) {
      alert(`no changes`);
      return;
    }

Since - if arrays identical - there is no need to perform any request...

Removed

Next step i decided to collect removed array items (if were any):


const removed = oldArr.filter(({id}) => !modifiedArr.find(item => id === item.id));

Im only checking which items i cannot find in modified array.

Added

Collect the new items:


const added = modifiedArr.filter(({id}) => !oldArr.find(item => id === item.id));

Here im checking exactly the opposite - which items i cannot find in the old array

Modified

Collect the modified items


const modified = modifiedArr.filter(modifiedItem => !oldArr.find(item => JSON.stringify(modifiedItem) === JSON.stringify(item)))

Here im checking which items are different from old Array by checking their json strings.
Note: the in all the cases - i taking advantage of filter function of an Array.

12/15/19

What is Session Authentication?

Intro

I want to post here some post for a simpletons like me, who sometimes discovers basic things about web they didn`t knew. For example - recently i asked myself: "what is exactly session?" and, shame on me - i discovered that i cannot give quick and clear answer. So i publishing here some facts i came upon while searching across the web:

Session

Some data stored at server (variable) which connected to some specific user, and "lives" when this user is connected to server. The old classic way to implement this behavior - by creating a cookie.
cookie - some piece of data that comes from server and stored at browser, it has it unique "id" given by server, and may have some other data (like expiration time). cookie should be sent at headers of each request. Server will check the cookie (or if it present) and this way server will now about user and if he is connected

Implementing Session Authentication using Express

Since express.js is the only server framework i feel comfortable with - i will do all the demonstrations using this framework.
So here is some simple server with '/login' and '/private' apis:


const express = require('express');
const app = express();
const port = 4000;
/* must use cors module for recieve requests from different domains */
app.use(require('cors')())

app.post('/login', (req, res) => res.send({user: 'Lukas'}));

app.get('/private', (req, res) => res.send({products: [{name: 'ZX10R'}]}));

app.listen(port, () => console.log(`Example app listening on port ${port}!`))

Pretty nice & simple, right?

Accessing Api`s from client

We have created a grate piece of software in previous step, but - what worth a server if nobody uses it?
In other words: lets create some client which will consume the our shiny apis!


async function bla() {

    const data = await fetch('http://localhost:4000/login', {
      method: 'POST',
      headers: {'content-type': 'application/json'}
    }).then(data => data.json())

    console.log(`hi ${data.user}!`);
    // passed login lets show some private stuff!
    const productsData = await fetch('http://localhost:4000/private', {
      method: 'GET',
      headers: {'content-type': 'application/json'}
    }).then(data => data.json())
          
   console.log(`product: ${productsData.products[0].name}`)
 
}
bla();

As you can see(if you copied the code correctly) - the data being fetched:

Restricting Private Api`s using session

Currently the '/private' api can accessed by everybody (and it is suppose to be private!), so lets make it accessible only to one who passed the login request:
We will do it using cookie-session npm package.
0. install 'cookie-session' lib (npm i cookie-session)
1. use basic configuration:


const cookieSession = require('cookie-session')
app.use(cookieSession({
    name: 'mysession',
    secret: 'your-secret',
    maxAge: 30 * 60 * 1000 // 1/2 hour
}));

2. we will add some data to our session once user passed login request (this code mimics fetching user details from DB)

app.post('/login', (req, res) => {
    req.session.user = 'Lukas';
    res.send({user: 'Lukas'});
});

3. add some middleware which will block (return 401 response) for someone who tried to access to any(except '/login') api when unauthenticated

app.use((req, res, next) => {
    if (req.url.includes('login')) {
        next();
        return; 
    }

    if (!req.session || !req.session.user) {
        res.clearCookie('mysession');
        res.status(401).send({error: 'unauthorized'});
        res.end();   
    }
});

One more thing! We need modify now also the client code:

async function bla() {
    const productsData = await fetch('http://localhost:4000/private', {
      method: 'GET',
      credentilas: 'include', /* each request should carry the cookie at its headers */
      headers: {'content-type': 'application/json'}
    }).then(data => data.json())
          
   console.log(`product: ${productsData.products[0].name}`)
}
bla();

Also at cors configuration - we need to specify "origin" to be trusted:

app.use(cors({
  origin: 'http://localhost:3000', /* host where you client runs */
  credentials: true,
}));

Now, open the "network" panel of chrome dev tools. You should should see the following:

Tada!!! you just make a client to talk to server using session !!!

Getting started with docker

It is very simple to get started usig docker. All you need to do-is download the docker desktop for your system Once you get docker syste...