Showing posts with label react. Show all posts
Showing posts with label react. Show all posts

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.

7/11/19

Hooks in React

What is this new React feature everybody is talking about?
Well, it seems the main problem it tries to solve is - how to use state inside functional components.
Functional... what?
Ok, lets start from the beginning:
Usually to build the component with React you should use the following syntax:

class HelloMessage extends React.Component {
  render() {
    return (
      <div>;
        Hello {this.props.name}
      </div>
    );
  }
}

It also called the class syntax
The Interesting thing is - this is not the only way. There is another way to create react component, it called functional :

function Example(props) {
   return  ( 
      <div>;
        Hello {this.props.name}
      </div>
     );
}

This way is much more simpler and clear and testable, because - it is just javascript function!
BUT! (There is always but) - one of the limitations of functional components is - that here you cannot use the "state" feature of React.
State - it is inner data of the component which is not accessible from the parent

class UserForm extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      username:'',
      lastname: ''
    };
  }
  render() {
    return (
      <form>
        username: <input value={this.state.username}/>  <br/>
        lastname:  <input value={this.state.lastname}/>
      </form>
    );
  }
}

ReactDOM.render(
  <UserForm  />,
  document.getElementById('user-form')
);

So now (from React 16.8 and later) you can use state feature event inside functional components (Yes You Can!)- by using following syntax:

import React, { useState } from 'react';

export default function Example() {
    const [userModel, setUserModel] = useState({username:'ss', lastname:'dd'});

    return (
      <form>
        username: <input value={userModel.username}/>  <br/>
        lastname:  <input value={userModel.lastname}/>
      </form>
    );
  }

Note that "setUserModel" is for modify the state:

<button onClick={()=>setUserModel({username: 'shlomo', lastname: 'shlomovitz'})}/>click</button>

Pretty Cool, huh?

12/30/18

Is it complicate to write unit tests for your React app?

Not at all!
All you need is to install jest testing framework:

npm add --D jest babel-jest babel-preset-env babel-preset-react react-test-renderer
Add "test" script to "scripts" section in package.json file

  "scripts": {
    "start": "webpack-dev-server --open",
    "test": "jest",
Now you have the testing environment configured and running.
And you can start with adding some very simple unit test
You can create it on some file which has ".test" in its name - like example.test.js

test('adds 1 + 2 to equal 3', () => {
  expect(1 + 2).toBe(3);
});
After running
npm test
the result will be something like this
Now nobody can say about you that you never wrote a single test

8/31/18

magic of the dontenv

Recently i tried to solve the following problem: how to get react app to get backend Url from environment variable. The embarrassing truth is that until now my code was full of statements like this:

    fetch('http://localhost:8000/login', {
        method: "POST",
        credentials: 'include',
        headers: {
            "Content-Type": "application/json; charset=utf-8"
        },
        body: JSON.stringify( data )
    })
As you may notice - the url is hardcoded. The problem here is that this code will run only at developer environment where server running on 127.0.0.1 ("localhost") ip. But what about production environment where backend url can be http://yourappname.herokuapp.com ?

DONTENV

It appears the very cool solution for this problem is to use dotenv node plugin. Using it give us ability to define all environment specific variables in .nev file:

REACT_APP_API_HOST=http://localhost:8000
Now inside your react code you can access this variable this way:

 const requestOptions = {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ username, password })
 };
 fetch(`${process.env.REACT_APP_API_HOST}/login`, requestOptions)
        .then(handleResponse)

CREATE REACT APP

Create react app tool supports using .env files (so there is no need to install dotenv). You can create different .env files for different environments: like .env.local and .env.production.
By default - the "serve"(npm start) task will use "local", while "build" (npm run build) - will use production... Cool!

11/7/16

Presentational VS Container Components In React

What are Container Components?

There is rule for writing good & clear code - single responsibility.
Each code unit should depend as less as possible on other components (Should to care only about its own business in a human terms).
Thus it is good practice to separate code to self-containing units

Container Components are components which contains the logic of the component. Usually it contains in its markup one or more presentational components.

Presentational components are "dumb" components - they take care only about look of some part of data.

Example

Here is searchbar component


//Search Bar
import React, { Component, PropTypes } from 'react';
import {Link} from 'react-router';

let ResultsList = ({results, loading}) => {
        
      let createItem = function(repo, idx) {
        return <li key={idx}><Link to={`/repo/${repo.owner.login}/${repo.name}`}>{repo.name}</Link></li>;
      };
      let list;
      if(results && !loading) {
        list = results.length==0  ? 'no items yet' : results.map(createItem);
      } else {
        list = <div className="loader">Loading...</div>;
      }
      return <ul className="results">{list}</ul>;
             
}
export default ResultsList;

This code better to be refactored into smaller sub (Presentational)components

Refactoring:



import List from './List'
import Loading from './Loading'
import NoItems from './NoItems'
let ResultsList = ({results, loading}) => {
        
      return (<div>
        <Loading loading={loading}/>
        <List results={results}/>
        <NoItems results={results} loading={loading}/>
      </div>);
             
}

Here you can see that instead place logic and markup in the same place I made ResultsList to be Container which contains Loading List and NoItems presentational components

see code in this repo

9/29/16

Redux - the blitz

I have heard a lot about redux but never had time to put my hands on it., so i guess the time is come - lets write something about redux.

So, what is redux?

To put the long terms short - it is a way to organize logic which changes the state (application data) of single page application. So if you asking yourself: is redux about to make me write less code? - Well, the answer is no, this is not the point. The point is - to make clear for developer who and why changed the state of app.

Three Basic Principles Of Redux

According to what the documentation say, three basic principles of redux are:
1. Single source of truth( single store )
2. State is read-only
3. Changes are made with pure functions (reducers)
For implementing the redux into our search app we will use react-redux library.

Lets Talk About Three Primary Modules Of React-Redux

Store

Most important module there all application data (or state) is stored:
initial state:


export default {
    results: [],
    loading: false
}

...
...
...
const rootReducer = combineReducers({
  results: searchResults,
  loading
});

export default rootReducer;

...
...
...
export default function configureStore(initialState) {
  return createStore(
    rootReducer,
    initialState,
    applyMiddleware(thunk, reduxImmutableStateInvariant())
  );
}

Actions

A way by which components can "talk" with store, explaining what exactly gonna happen.
search action:


export function loadResultsSuccess(results) {
  return { type: 'LOAD_RESULTS_SUCCESS', results};
}

export function loadResults(query) {
  return function(dispatch) {
     let url = "https://api.github.com/search/repositories?q="+query+"+language:typescript&sort=stars&order=desc";
     
     return  fetch(url)
        .then(function(response) {
         return response.json();
        })
        .then((result) => {
            let results;
            if (result.items.length !== 0) { 
               results = result.items.slice(0,5);
            } else {
               results = [];
            }
            //DISPATCH RESULTS
            dispatch(loadResultsSuccess(results));
        });
  };
}

Reducers

Should get the info from action and return new state object which generated from the original state, but without mutating it.


import initialState from './initialState';

export default function searchReducer(state = initialState.results, action) {
  switch (action.type) {
    case 'LOAD_RESULTS_SUCCESS':
      return action.results;

    default:
      return state;
  }
}

Make The Appllication To Work With Stuff Was Just Created

Lest modify our SearchPage so, it will dispatch "search" action instead of calling "loadResults" method (which duty was to load the search results from the request and put the to the state):
SearchPage component:


import React, { Component, PropTypes } from 'react'
import SearchBar from './mySearchBar';
import ResultsList from './myResultsList';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import * as searchActions from './actions/actionSearch';

class SearchPage extends Component {
      constructor(props) {
        super(props)
      }

      handleSearchTextChange(txt) {
        this.props.loadResults(txt);//<---HERE      
      }

      render() {
        const {results, loading} = this.props;
       
        return (
          <div>
            <SearchBar onChange={this.handleSearchTextChange.bind(this)}/>
            <ResultsList results={results} loading={loading}/>
          </div>
        );
      }
};
    

function mapStateToProps(state, ownProps) {
  return {
    results: state.results,
    loading: state.loading
  };
}

function mapDispatchToProps(dispatch) {
  return {
    loadResults: bindActionCreators(searchActions.loadResults, dispatch)
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(SearchPage);   

See the full code on the repo

8/16/16

React - Organizing the project

How To Organize the Project

Yes, we created little app using the react library. For simplicity i used (already removed in latest babel version) browser module of babel-core which compiles the react in the browser. Thats not ideal way of writing react aps(compiling in the browser is heavy operation that can be done using some task running tool instead).

Using Webpack

There are many tools which can do the react compilation job. One of those tools is a well known webpack which we will use for this post

Installing Webpack

For be able to run webpack you must have nodejs installed on your machine.
Lets run this command in the console for install webpack globally


   npm install webpack -g
Now you able to bundle your javascript files into one js file named "bundle.js" with single console command

webpack ./app.js app.bundle.js
Instead of specify parameters in the command line, better attitude is to specify them in wepack.config.js file:

 module.exports = {
     entry: './src/app.js',
     output: {
         path: './bin',
         filename: 'app.bundle.js'
     }
 };
Tha way the command is way more shorter:

webpack //thats it

Dependencies

Since we will create react application we will need to install various dependencies (like babel) which will help us to compile and run our project. It is good practice to store list of dependencies in package.json file, so npm module of nodejs will be able to install them at once using single command npm install.
For creating package.json file run:


npm init 
Will promp you with various questions about your project, currently you may answer "yes" (by pressing ENTER) on all of them. finally package.json file will be created in your root directory.
From now each module we will install - will be listed in the package.json file:

 npm i react react-dom --save
package.json :

{
  "name": "webpack project",
  "version": "1.0.0",
  "description": "",
  "main": "webpack.config.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "react": "^15.3.0",
    "react-dom": "^15.3.0"
  }
}

babel

Since react contains some features that not supported by some browsers (like jsx) we will use babel library:


npm install --save-dev babel-loader babel-core babel-preset-react
So our wepack.config.js file will finally look like:

 module.exports = {

     entry: './src/app.js',
     output: {
         path: './out',
         filename: 'app.bundle.js',
     },
     module: {
         loaders: [{
             test: /\.js?|\.jsx?$/,
             exclude: /node_modules/,
             loader: 'babel',
             query: {
                presets: ['react']
             }             
         }]
     }
 }
Now our project configured in the way so when user runs

webpack
command in the console-
it compiles every javascript (.js) file and it puts the results inside out directory in single file app.bundle.js, isnt that cool?

Project Structure

After we created the minimalistic configuration, lets take a care about files that are part the project itself:
app.js file where we will place our (need to be compiled) javascript code (lets put it in the "src" directory) and index.html - the main html file of our single page app which will refer the compiled results of javascript:


 <!DOCTYPE html>
 <html>
     <head>
         <meta charset="utf-8">
     </head> 
     <body>
         <div id="example"></div> 
         <script  src="out/app.bundle.js" charset="utf-8"></script>
     </body>
 </html>
Now lets paste the app code into the app.js file

var React = require('react');
var ReactDOM = require('react-dom');
var $ = require("jquery");

//Search Bar
var SearchBar = React.createClass({
  
  onChange: function(e) { 
    // console.log(e.target.value)
    this.props.onTextChange(e.target.value);
  }, 
  render: function() {
    return (
      <div><input placeholder="type search term" onChange={this.onChange}/></div>
    );
  }
});   
// Results List
var ResultsList = React.createClass({

  render: function() {
    
      var createItem = function(item, idx) {
        return <li key={idx}>{item.text}</li>;
      };
      var list;
      if(this.props.results && this.props.results.length && !this.props.loading) {
        list = this.props.results.map(createItem);
      } else if(this.props.results.length==0  && !this.props.loading){
        list = 'no items yet';
      } else if(this.props.loading) {
        list = <div className="loader">Loading...</div>;
      }
      return <ul className="results">{list}</ul>;
    
  }
});
//Search Page
var SearchPage = React.createClass({
  getInitialState: function() {
    return {results: [],loading:false};
  }, 
  loadResults: function(query){
    this.setState({loading:true});
    
    var url = "https://api.github.com/search/repositories?q="+query+"+language:typescript&sort=stars&order=desc";
    $.get(url)
    .then(function (result) {
      
      if(result.items.length!==0){ 
        var results = result.items.map(item => { 
          return   {text: item.name}
        }).slice(0,5);
        
      }
      this.setState({loading: false, results:results});
    }.bind(this));//<-- important to bind this for use "setState"
    
  },
  handleSearchTextChange: function(txt){

    this.loadResults(txt)
    
  },
  render: function() {
    return (
      <div>
        <SearchBar onTextChange={this.handleSearchTextChange}/>
        <ResultsList results={this.state.results}  loading={this.state.loading}/>
      </div>
    );
  }
});
    
ReactDOM.render(
    <SearchPage />,
    document.getElementById('example')
);
module.exports = SearchPage;

jquery

the last thing left is to add jquery library to the project


 npm install --save jquery 
After jquery is included and project compiled you can run it with

webpack-dev-server
The developer server of webpack.
Here is link to repo with the code.
Hope you have fun reading

8/9/16

React - Building Search App (Part 5)

Ajax

In previous parts we have created react functioning application that consists from 3 components, which can communicate and respond to events, but what kind of app is this if it only displaying some static data?
Of course we need to do to some server request for bring some real data.
Unlike angular that has its own http service, react doesn't have helper functions for ajax and this due to its convention - to be lighter as possible.
Fot quickly write a code that makes ajax request we will use good old jquery library. Instead of build some server for serve us data we will take an advantage of awesome github api
For example following api will get you repositories those names contains some characters you specified as search param:


   var url = "https://api.github.com/search/repositories?q="+query+"+language:typescript&sort=stars&order=desc";
   $.get(url, function (result) {
      //result.items will contain repositories corresponding to query
   })

Make a Request

Now the only thing left is to get the request to play with the other code:


    //Search Page
    var SearchPage = React.createClass({
      getInitialState: function() {
        return {results: [{text:'some result'}]};
      }, 
      loadResults: function(query){
        
        var url = "https://api.github.com/search/repositories?q="+query+"+language:typescript&sort=stars&order=desc";
        $.get(url, function (result) {
          if(result.items.length!==0){
            var results = result.items.map(item => { 
              return   {text: item.name};//<--- for our app we need each item to have "text" property
            }).slice(0,5);
            this.setState({results:results});
          }
        }.bind(this));//<-- important to bind this for use "setState"
      },
      handleSearchTextChange: function(txt){

        this.loadResults(txt)//<--- calling the request with search term passed from search input
       
      },
      render: function() {
        return (
          <div>
            <SearchBar onTextChange={this.handleSearchTextChange}/>
            <ResultsList results={this.state.results}  />
          </div>
        );
      }
    });
Note that for pass the results to ResultsList component we need only to modify the state using the setState method.

view the code
Hope you have fun reading

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...