Small app to practice redux state management with Mosh https://codewithmosh.com/courses .
git clone https://github.com/amopho/redux.git
To install packages from package json
a js bundle that would throw js code into app json in dist directory.
Configure webpack.config.json
const path = require("path");
module.exports = {
entry: "./src/index.js",
output: {
filename: "app.js",
path: path.resolve(__dirname, "dist")
},
devServer: {
contentBase: path.join(__dirname, "dist"),
port: 9000
},
mode: "development"
};
Package JSON prerequisetes:
{
"name": "redux-starter",
"version": "1.0.0",
"description": "Redux Starter Project",
"main": "index.js",
"scripts": {
"start": "webpack-dev-server --config ./webpack.config.js"
},
"keywords": [],
"author": "Mosh Hamedani",
"license": "ISC",
"devDependencies": {
"webpack": "4.41.6",
"webpack-cli": "3.3.11",
"webpack-dev-server": "3.10.3"
}
}
Structure of working directory dist-> index.html src-> index.js root-> package.json, webpack.config.js
A JavaScript library to build web apps what makes React so special:
- Components
- State
Install and clear the created directory from any files and code one doesn't need. For example:
- in index
- in app
- in public
- in src
$ npx create-react-app <app-name>
$ cd <app-name>
$ npm i node-sass
$ npm start
Default structure for saas:
- main.
- css.
Installing gh-pages package
$ npm install gh-pages
Adding homepage into package.json
"homepage": "http://<your-gh-username>.github.io/<your-repo-name>"
Adding some more scripts into package.json
"scripts": {
//...
"predeploy": "npm run build",
"deploy": "gh-pages -d build",
}
Lastly run build script to generate build dir
$ npm rum build
$ npm run deploy
$ npm i --save @fortawesome/fontawesome-svg-core
$ npm install --save @fortawesome/free-solid-svg-icons
$ npm install --save @fortawesome/react-fontawesome
$ npm install --save @fortawesome/free-brands-svg-icons
$ npm install --save @fortawesome/free-regular-svg-icons
$ npm install react-icons --save
Import icons into each component.
$ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
$ import { faCoffee } from '@fortawesome/free-solid-svg-icons'
$ const element = <FontAwesomeIcon icon={faCoffee} />
$ ReactDOM.render(element, document.body)
$ npm install react-router-dom
npm i axios
$ npm install react-bootstrap bootstrap@5.0.1
$ npm install normalize-scss
npm i emoji-picker-react
import React, { useState } from 'react';
import Picker from 'emoji-picker-react';
const App = () => {
const [chosenEmoji, setChosenEmoji] = useState(null);
const onEmojiClick = (event, emojiObject) => {
setChosenEmoji(emojiObject);
};
return (
<div>
{chosenEmoji ? (
<span>You chose: {chosenEmoji.emoji}</span>
) : (
<span>No emoji Chosen</span>
)}
<Picker onEmojiClick={onEmojiClick} />
</div>
);
};
is cutting a bigger problem into a bunch of smaller and reusable funtions which take an input and troe a result. The code written by this way is
- more concise,
- easy to debug and tests,
- scalabale. Javascript is not a pure functional programming language. It is a multi paradigm language.
If we don't want to call a function just pass a reference to it. Funtions are first type citizens, we can treat them like normal variables. By calling a funtion we sayHello() we will get an result- string. But we cna pass a function as an argument to another function.
function sayHello() {
return "Hello World!";
}
let fn sayHello;
fn();
sayHello();
// not calling the function just passing it as a references
//function greet(fnMessage) {
//console.log(fnMessage());
//greet(sayHello)
//}
//returning a function instead of a string. Higher order funtion will take another function as a argument or returns it, or both.
function sayHello() {
return function() {
return "Hello World";
}
}
// examples of higher order funtions are well known, like map()
let numbers = [1,2,3];
numbers.map(number => number *2);
// setTimeout as another example because it takes a function as an argment as well
// nonfunctional approach makd funtional way
let output = " Javascript ";
let output = "<div>" + input.trim() + "</div>;
// things we need to do are writing a two small nd reusable functionss:
// trim
// and wrap into div
// i use const as i wouldn't like to reasign it laters
const trim = str => str.trim();
//render the string dinamically with backtick with $ sign as a template
const wrapInDiv = str => `<div>${str}</div>`;
// we may add another function to lowercase the result
const toLowerCase = str => str.toLowerCase();
// now we use Function Composition in functional programming, but it will result in a plenty of paranthesis we need to close
const result = wrapInDiv(toLowerCase(trim(input)));
// Lodash - JS library to simplify the code which has a lot of funtions for functional programming
import {compose, pipe} from "lodash/fp";
Lodash - JS library to simplify the code which has a lot of funtions for functional programming
npm i lodash
Into index.js write FP stays for Functional Programming
Lodash to simplify the functional programming, let's get rid of all those unnecessary paranthesis 3 arguments for compose- an higher order function which takes another functions as arguments and returns a new function ```javascript const transform = compose(wrapInDiv, toLowerCase, trim);
But still, it is not that preety piece of code as we need to read the functions order from rigth to left, to solve this problem we will use pipe and pass funtions in the order we are applying. pipe builds a pipeline the output of each function ends up of being an output of the next one.
In our example case the second pipeline output is a lowercase string, the second parameter is empty, and that's why the console log says undefined, by giving the div as a type element to the wrap we get another error: "expected a function", it produces a string, but every argument given to the pipe has to be function. We need a funtion with a single parameter instead of two parametres - currying solves this problem.
After applying currying to wrap("div we get a funtion instead of a string"). Now we can pass this function into a pipe.
```javascript
import {compose, pipe} from "lodash/fp"
Currying function named after Huskel Curry we remove unnecessary duplications like in the example where the function does almost the same, except it uses different element. Let's parametrise it.
/* const wrapInDiv = str => `<div>${str}</div>`;
const wrapInSpan = str => `<span>${str}</span>`; */
// we rewrite a function with two parametres with help of Currying technique:
const wrap = (type, str) => `<${type}>${str}</${type}>`;
That function when used in a Pipeline would produce a string, and should give a function. This is because one of the functions in the pipeline has two parametres instead of one. We can obtain one parameter function from that one with two parametres by currying: Currying is a function that allow us to grab 'N' argumets and create a function with a single argument.
function add(a) {
return function(b) {
return a+b
}
}
Currying the two arguments function
// const wrap = (type, str) => `<${type}>${str}</${type}>`;
const wrap = type => str => `<${type}>${str}</${type}>`;
Renders the same results with given arguments, every single time the component re-renders. It can't contain:
- random values,
- date/ time
- no global state (dom, db..)
Error: lodash/fp - in order to solve the import problems the index.js should be renamed as index.mjs or package.json should have "type": module added
In redux we store the application in a single js object called the store. It has to be accesible for every single element in the UI
For the e-commerce shop, our store objrct will have properties like:
{
products: [],
categories: [],
cart: {},
user: {}
}
Remeber one is not able to mutate the state as the redux is built on top of functional programming rules. So in order to update the state in the store we should build a function that takes the store (immutable object) as an argument and updates it by returning updated store. Or to use the immutability libraries like immer or immutable.js
//not possible
store.currentUser = {name: "Name"}
// just:
function reducer(store)
{
const updated = {...store}
}
How does the reducer (event handler) knows which instance eof the store to update? For that one we need an action (event)- js object that describes what just happened. it is the second parameter of the reducer fucntion.
App may have one or more reducers, each responsible for updating a specific slice of store.
These reducers are pure functions, not changing the data.
Take an action and dispatch it, and let the store to know about the action. Store has this dispatch method, and forwrds the action to the reducer. We are not calling the reducer, we work with the store who is responsible for calling the reducer. It computes the new state and returns it. The store injects the state, and notifies the UI components. They will pull the updated data and rerender. Dispatch is like a single entry point for every action
Steps for Redux Architecture
- Designing a store
- Define Actions
- Create a Reducer
- Store
Install the Redux:
npm i redux@4.0
For a bug tracker application we would need to Maintain The List of Buggs the state:
Array of bug objects:
[
{
id: 1,
description: "",
resolved: false
},
{},
{},
{}
]
In the real life however it would rather be looking like object with multiple properties containing arrays (list) of bugs, user...
{
bugs: [
{
id: 1,
description: "",
resolved: false
},
{},
{},
{}
],
currentUser: {}
}
Remember Action is a simple JS Object that expalins eawhat just happened Mark a Bug as Resolved, Add a Bug, Delete a bug
Redux would search for the type property in your action object. So it is an obligatory property in your action object.
// coommon convention for a descriptive type in Redux is a capitalizing, with underscore
// description is a data linked with this type - like user describing the bug
{
type: "ADD_BUG",
description: ""
}
// it is a good practice to add a payload to describe all the data of the action - a solution from facebooks flux architecture
{
type: "bugAdded",
payload: {
description: ""
}
}
// minimum information on the data - all we need to identify a bufg is its ID
{
type: "bugRemoved",
payload: {
id: 1
}
}
It has to be a serializable (str, numbers but they are less descriptive), which means we need the state to be able to be stored on the disc. Than we may reload it later.
In a src folder add another file reducer.js
In a reducer we are cerating logic that fits to our app Reducers have to be pure functions- by giving the same arguments (state, action) it would return the same results The job of reducer is to return the new state with this action. We have an initial empty array of bugs, to which we will be adding. First copy all the bugs from the current state and add a new object with a bug, It would be nice to have a counter as well, set to 0 at the beginnig. Important thing is we need to make sure that payload carries a minimum of descriptive information about the new object. So we give a description, and the other properties should be computed.
store made from imported from redux createStore() from