Saturday, February 25, 2023

useMemo Hook

useMemo prevents unnecessary rendering of child components:

import React, { useState, useEffect, useMemo } from "react";
 
const Child = ({ param }) => {
  useEffect(() =>
    console.log("child rendered at ", new Date().toLocaleTimeString())
  );
  return <div>prop in child: {param}</div>;
};
 
const App = () => {
  const [counter, setCounter] = useState(3);
  const [childProp, setChildProp] = useState(30);
 
  const handleChange = () => setCounter((prev) => prev + 1);
  const handleChangeChild = () => setChildProp((prev) => prev + 10);
 
  const memoChild = useMemo(() => <Child param={childProp} />, [childProp]);
 
  return (
    <>
      {counter}
      <br />
      <button onClick={handleChange}>change parent counter</button>
      <button onClick={handleChangeChild}>change child prop</button>
      {memoChild}
    </>
  );
};
 
export default App;
 

Monday, October 31, 2022

React useState Hook

Working with Array of Objects and Object


another example with delete update



Thursday, September 1, 2022

NodeJS and ES6

Bable can transpile ES6 code to JS code that can be processed by Node.

Folder and file structure:






Here is package.json
{
  "name": "node-server-using-es6",
  "version": "1.0.0",
  "description": "Node Server compiles code to ES6 using babel",
  "main": "lib/index.js",
  "scripts": {
    "build": "rimraf build && babel src -d dist",
    "start": "nodemon --exec babel-node src/index",
    "prod": "npm run build && node dist/index.js"
  },
  "license": "ISC",
  "devDependencies": {
    "@babel/cli": "^7.2.3",
    "@babel/core": "^7.4.0",
    "@babel/node": "^7.18.10",
    "@babel/preset-env": "^7.4.2",
    "@babel/register": "^7.4.0",
    "nodemon": "^1.17.5"
    "rimraf": "^3.0.2"
  },
  "dependencies": {
    "express": "^4.18.1"
  }
}

Here is .babelrc
{
  "presets": ["@babel/preset-env"]
}

Here is src/index.js in ES6
import express from 'express';
const app = express();
const PORT = 4000;

app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.listen(4000, () => {
  console.log(`app is running on ${PORT}`);
});

for production npm run build this will populate build folder
for development npm start

Thursday, January 27, 2022

Launch Browser in Code

For React project create folder .vscode an in it launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "chrome",
            "request": "launch",
            "name": "Launch Chrome on localhost:3000",
            "url": "http://localhost:3000",
            "webRoot": "${workspaceFolder}/src",
            "userDataDir": "${workspaceFolder}/.vscode/chrome"
        }
    ]
}

This will create separate browser for development.

Friday, January 21, 2022

React callback

Passing function as argument and callback in setState.
import React from 'react';

export default class App extends React.Component {
  state = {
    users: [],
    status: 'initial',
  };

  componentDidMount() {
    this.getUsers();

    this.myCoolFunction('first', 'second', (result) => console.log('yay the result is', result));
    this.myCoolFunction('first', 'second');
  }

  myCoolFunction = ( param1, param2, callback = () => console.log("function is not passed") ) => {
    let result = 'do stuff with ' + param1 + ' ' + param2;
    console.log(callback)
    console.log('just before callback(result)');
    callback(result);
  };

  getUsers = async (id) => {
    setTimeout(() => {
      this.setState({
        status: 'searching',
        users: [],
      });
    }, 3000);

    setTimeout(() => {
      this.setState(
        {
          status: 'done',
          users: [
            { id: 23, name: 'gdfgd' },
            { id: 24, name: 'weqweeq' }
          ],
        },
        () => this.state.users.map((user) => console.log(user.name))
      );
    }, 5000);
  };

  render() {
    if (this.state.status === 'initial') {
      return <div>ini</div>;
    }
    if (this.state.status === 'searching') {
      return <div>searching</div>;
    }
    if (this.state.status === 'done') {
      return (
        <ul>
          {this.state.users.map((user) => (
            <li key={user.id}>{user.name}</li>
          ))}
        </ul>
      );
    }
  }
}

Monday, November 22, 2021

Gulp & React

Replace & Delete files in build folder using Gulp gulpfile.js
 
const gulp       = require('gulp');
const replace    = require('gulp-string-replace');
const rimraf     = require('gulp-rimraf');

function changeLang() {
    const searchFor = '<html lang="en">';
    const replaceBy = '<html lang="de">';
  
    return gulp.src('./build/*.html')
      .pipe(replace(searchFor, replaceBy))
      .pipe(gulp.dest('./build'));
}

function deleteFiles () {
  return gulp
    .src('./build/static/js/*.*.txt', { read: false })
    .pipe(rimraf());
}

exports.change_lang = changeLang;
exports.delete_files = deleteFiles;

package.json

{
  "name": "my-app",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.11.4",
    "@testing-library/react": "^11.1.0",
    "@testing-library/user-event": "^12.1.10",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-scripts": "4.0.3",
    "web-vitals": "^1.0.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build && npm run gulp-change-language && npm run gulp-delete_files",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "gulp-change-language": "gulp change_lang",
    "gulp-delete_files": "gulp delete_files"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "gulp": "^4.0.2",
    "gulp-rimraf": "^1.0.0",
    "gulp-string-replace": "^1.1.2"
  }
}

Test gulp

npm run build

Friday, November 19, 2021

CSP and Dynamically Generated JavaScript tag

CSP HTTP header and Meta Tag must be synchronized. There must be two hashes one for hard coded JavaScript other for generated JavaScript tag. 

index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Content Security Policy Demo</title>
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; font-src 'self' ; img-src 'self' https://images.unsplash.com ; script-src 'self' 'sha256-B/8L7bCoRRLbK80hqzXcLc/NSWDtHVADDkdElr18Q2U=' 'sha256-BQBWQMByG186hXi3h+x/zUK8eOCr3iy91FDzS9uKCNs='; style-src 'self' https://cdn.jsdelivr.net; frame-src 'self'">
    <link rel="stylesheet" href="style.css" />
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css"
      integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2"
      crossorigin="anonymous"
      />
    <script src="node_modules/jquery/dist/jquery.min.js"></script>
  </head>
  <body>
    <script type = "text/javascript" >
      $(document).ready(function() {
         $('div.grid').after('<div>new element</div>');
      });
      let newScript = document.createElement("script");
      let inlineScript = document.createTextNode("alert('Hello World!');");
      newScript.appendChild(inlineScript);
      $(document.body).append(newScript);
    </script>
    <div class="box">
      <div class="grid">
        <img
          src="https://images.unsplash.com/photo-1535089780340-34cc939f9996?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=80"
          alt=""
          />
        <img
          src="https://images.unsplash.com/photo-1587081917197-95be3a2d2103?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=80"
          alt=""
          />
        <img
          src="https://images.unsplash.com/photo-1502248103506-76afc15f5c45?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=80"
          alt=""
          />
      </div>
    </div>
  </body>
</html>
server.js
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const app = express();
 
app.use(function (req, res, next) {
  res.setHeader(
    'Content-Security-Policy',
    "default-src 'self'; font-src 'self' ; img-src 'self' https://images.unsplash.com ; script-src 'self' 'sha256-B/8L7bCoRRLbK80hqzXcLc/NSWDtHVADDkdElr18Q2U=' 'sha256-BQBWQMByG186hXi3h+x/zUK8eOCr3iy91FDzS9uKCNs='; style-src 'self' https://cdn.jsdelivr.net; frame-src 'self'"
  );
  next();
});
 
app.use(bodyParser.json());
app.use(express.static(path.join(__dirname)));
 
app.get('/', (req, res) => {
  res.sendFile(path.join(__dirname + '/index.html'));
});
 
const server = app.listen(process.env.PORT || 5500, () => {
  const { port } = server.address();
  console.log(`Server running on PORT ${port}`);
});
 
package.json
{
  "name": "csp-javascript",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "start": "npx nodemon server.js"
  },
  "dependencies": {
    "body-parser": "^1.19.0",
    "express": "^4.17.1",
    "jquery": "^3.6.0"
  },
  "devDependencies": {
    "nodemon": "^2.0.6"
  }
}