Releases: yunusparvezkhan/adprava
Version 4 Anaros 🍍
The version 4 of the Adprava app has a new page CounterPage
, which is a basic counter component, famous in ReactJS World. The aim of this page is learning and practicing Reducer functions in ReactJS. In addition to that this page is also a practice for custom hooks, as all the logics behind the counter component is migrated to a reusable custom hook.
Reducer Functions in ReactJS
Introduction :-
Reducer is an alternative to the useState
hook in React. Reducer is also a hook and imported as useReducer
from react
. This hook is a better state manegement system for larger applications, as using simple useState
in larger applications can be complicated and confusing due to its "separate declaration for separate state" theory. To address this issue, useReducer
gives a distinguish feature of initiating all the states inside a object, passed to useReducer()
's second argument. Now, although the state updation process may seem like overwhelming, it is actually more descriptive and formal in my sense. To update a state, the specific function dispatch()
is to be called, with an value, which then goes to a defined function, which the developer then decide what to do with that value. These things may go over the head of many people, but we will discuss in details on what and how to use useReducer
.
Use of the useState
hook
We use useState
hook to create states like class components are used to have. To declare a state we write on the top of the code inside the function component,
const [exampleState, setExampleState] = useState('The Default Value and default Data Type');
So the entire component may look something like,
const App = () => {
const [exampleState, setExampleState] = useState('The Default Value and default Data Type');
// More code
// More code
return(
<div>
{exampleState}
</div>
);
};
The first value exampeState
is the variable for the state. The second value setExampleState
is the setter function for that state. You must have noticed that to call the state, we do not need to mention this.state.exampleState
, rather we directly called the state by exampleState
, as like calling a variable. Then the setter function can be also directly called as
setExampleState("New Value");
Declaration of useReducer
hook
The declaration of the useReducer
hook is much similar to the useState
hook. To declare useReducer
hook, we write on the top of the code inside the functional component,
const [state, dispatch] = useReducer(reducer, {
state1:'Default value of state1',
state2:'Default value of state2',
state3:5,
})
There is nothing special about the terms state
and dispatch
, but as international convention almost every developer uses these default terms. Then inside the useReducer()
function call, the first argument is a function, that should be declared somewhere in the file, and the second argument is the entire state. It definitely can be an object or any other data types. The variable state
is reference to the entire state, declared in the second argument of the useReducer()
function call.
Accessing the useReducer
state
To access the state created by using the useReducer
hook, the varibale assinged to it, can be used
Example - The component with the state declaration by using useReducer
hook in the previous point may look like,
import React, { useReducer } from "react";
export default const App = () => {
const [state, dispatch] = useReducer(reducer, {
state1:'Default value of state1',
state2:'Default value of state2',
state3:5,
})
// Some more code
return(
<div>Some JSX</div>
)
}
Now here inside the return()
function, if we want to access any of the state values, we may write,
return(
<div>
State 1 --> {state.state1}
State 2 --> {state.state2}
State 3 --> {state.state3}
</div>
)
And this will give us back the values of corresponding states.
Updating the useReducer
state
Remember we have passed reference of a function reducer
as the first argument of useReducer()
function call in the useReducer
declaration. That function need to be initiated somewhere in our file. By convention developers feel it right to declare that function at the top of the file, before the component function. So now, with the reducer()
declaration our file may look something like,
import React, { useReducer } from "react";
const reducer = () => {
// Some logic to handle state dispatch
}
export default const App = () => {
// useReducer declaration
const [state, dispatch] = useReducer(reducer, {
state1:'Default value of state1',
state2:'Default value of state2',
state3:5,
})
// Some more code
return(
<div>
State 1 --> {state.state1}
State 2 --> {state.state2}
State 3 --> {state.state3}
</div>
)
}
To update the state, the dispatch()
function is to be used. And the value passed in the first argument of the dispatch()
function will be recieved in the second argument of the reducer()
function. In total the reducer()
function recieves two arguments, state
and action
. Whenever we call dispatch()
, it calls the reducer()
. Then whatever the reducer()
function will return, would be the new state. So, we can dispatch an object like,
{
type:"UPDATE_STATE1",
payload:"The New Value",
}
for updation of state1
, now we have to recieve this in the reducer()
and return a new state.
import React, { useReducer } from "react";
const reducer = (state, action) => {
// Some logic to handle state dispatch
// checking dispatch types before returning new state
if (action.type === "UPDATE_STATE1") {
return {
...state,
state1:action.payload,
};
};
};
export default const App = () => {
// useReducer declaration
const [state, dispatch] = useReducer(reducer, {
state1:'Default value of state1',
state2:'Default value of state2',
state3:5,
})
const handleCSTabc = () => {
// Dispatching an object with a type prop
dispatch({
type:"UPDATE_STATE1",
payload:"ABC",
})
}
return(
<div>
State 1 --> {state.state1}
State 2 --> {state.state2}
State 3 --> {state.state3}
<button onclick={handleCSTabc} >Change State1 to abc</button>
</div>
)
}
As it can be seen in the examle code, that we may dispatch an object with a predefined type
property, and then check the type
in the reducer()
function before returning a new state. And this process can be continued and further nested for more states, and more updates.
Full Changelog: v3...v4
Version 3 Tormuj 🍉
The version 3, Tormuj
has a new page TablePage
. This page demonstrates two new components Table
and SortableTable
. These components are for rendering dynamic Tables by passing data as props.
The Table
component
Table
is a component to render a decent table. This table is dynamic and controlled by a dataset, in a other file or in a backend server. The Table
component recieves 3 props as data
, config
and KeyFn
. The Table
component renders an unsorted table according to the data recieved from these 3 props. Two of them are for data and another for an unique-key generator function.
The data
prop
The data
prop accepts an array of objects. Where each object is a row, and each element inside a object should be values for all the columns on that row.
For example the object for the first row in the first diagram would be,
{
name:'Orange',
color:'Orange',
count:5,
}
and the Sq Count
can be dynamicly calculated in the config
prop (We will discuss about it later). So, the data
prop should recieve a array of such objects.
The config
prop
The config
prop also accepts an array of objects. But here each object is a configuration of each column. The properties of these objects are more selective and limited. The properties that the Table
component recognises in a object inside config
array are,
label
render
sortValue
header
label
label
is the header of the column. This property should be a string, and will be rendered inside a <th>
tag, like -
<th>{config.obj.label}</th>
render
render
property should be a function. This property tells the Table
component that which property from data
's objects should be rendered in this column. The function accepts one argument row
, (that is any single object from data
) and should return the property that should be rendered in this column as row.property
. Demonstration is given in the diagram above and in the example below.
{
label: 'some heading',
render: (row) => row.name,
...
}
sortValue
This property in config
objects is optional. If the column contains data, that can be logically sorted (i.e numbers, strings) then this property should be pressent in that column's config
object. This property accepts a function, which recieves w single objects from data
and returns that property that should be sorted,when user clicks on the header.
{
label: 'some heading',
render: (row) => row.name,
sortValue: (row) => row.name,
}
header
This property is also super optional. This property can be used in times when developers want to show the heading inside any other tag or in any other style. In other words, this property can be used to control the behaviour of the heading of the column. This property accepts a fucntion, where it recieves nothing, but returns a jsx tag. This tag is expected to contain a heading, because this tag would be rendered in the column's heading's place.
{
label: 'some heading',
render: (row) => row.name,
sortValue: (row) => row.name,
header: () => <h1 style={{color:'#505050'}}>some heading</h1>
}
The KeyFn
prop
The KeyFn
prop accepts a function, which recieves an argument, that is a object from either data
or config
and should return a unique value according to the object passed. This value is used as unique key in mapped elements.
Example
const keyFn = (obj) => {
if (obj.name) {
return obj.name;
} else if (obj.label) {
return obj.label;
}else {
return obj[1];
}
}
The SortableTable
component
SortableTable
is a component to render a table with sortable optionalities. This table is dynamic and controlled by a dataset, in a other file or in a backend server as like the Table
component. The SortableTable
component has exactly the same data loading as the Table
component, which means it recieves 3 props as data
, config
and KeyFn
. The SortableTable
component renders an unsorted table at first, but if clicked on the column headers, it renders the rows in acsending order according to the values of that column, then in next click it sorts in descending order and then on another click back to unsorted order. If clicked on any other headers, the sort-order cycle breaks and restarts. The 2 of the props are for data and another for a key generator function. There is no need to put repeated informations here, read about the data-loading and the role of the props above in the Table
components' details.
Full Changelog: v2...v3
Version 2 Angur 🍇
Angur 🍇 (Version 2)
The version 2 of the Adprava
app has a new page ModalPage
. ModalPage
is a demonstration of a component Modal
. Modal
is basically a popup box component. The Modal
popup box has a bold centered title, a text for giving some message to the user and two buttons, one for conformation, called Proceed
and another for cancelation, called Cancel
. As we go along this Note, we will explore more technical details about it.
The component Modal
Modal
is basically a popup box component that can be invoked from any other component and can be used for asking for any conformation from the user. This component sends back boolean message to it's parent component according to whether the user accepted the proposal or not.
Props
The Modal
component accepts 4 props,
- heading
- message
- closeModal
- modalConformation
heading
The prop heading
accepts a string
that is supposed to be a short title of the Modal PopUp box.
message
The prop message
also accepts a string
that is rendered below the title in a normal font style and size like a paragraph. This prop is supposed to be used for passing the main textual content of the modal.
closeModal
The prop closeModal
accepts a function, that should close the appearance of the Modal
component in the parent component. This function should be passed to the Modal
component with this prop. Whenever user clicks on any of the buttons or outside the modal in the background this function is invoked.
modalConformation
The modalConformation
prop sends back a boolean parameter. The modalConformation
prop should be given a handler function, that would have a conditional. If the user clicks on the Proceed
Button, the modalConformation(true)
function is invoked. And if the user clicks on the Cancel
Button, the modalConformation(false)
function is invoked. The parent component can take these boolean values and do some processing with a handler function.
The Page ModalPage
The ModalPage
is just a demonstration of the Modal
component. This page has a heading like all the other pages. This page has a Button
component that invokes a function, which makes a boolean state modalsts
true
in the page. When this state is changed, a useEffect function invokes, which sets the Modal
component's styles to be visible. This page also contains a handler function for the closeModal
prop accepted by Modal
component as handleCloseModal()
. This function basically just makes the modalsts
state false
. Then this page also has the handler function for modalConformation
prop accepted by Modal
component as handleModalConformation(status)
. This function just just updates a state modalConf
with the boolean value it gets from Modal
. After this there is a useEffect
function that invokes when the modalConf
state changes and does some job according to the message it gets from the Modal
.
Full Changelog: v1.2...v2
Version 1.2
Replaced the ButtonPage
's diagram, as it was a mistakenly put a entire screenshot.
Full Changelog: v1.1...v1.2
Version 1.1
Changed names in couple of places, ButtonPage
is now ButtonsPage
and it's url is /Buttons
, not /Button
. This clearly makes more sense.
Full Changelog: v1...v1.1
Version 1 Narkel 🥥
The Version 1 Narkel
contains Three Pages, namely Dropdown, Accordion and Button. Descriptions are given about these pages in the README.md in the root directory.
Accordion v1
Version one of the accordion component. In this version multiple accordion items can be expanded together, which will be changed in the future versions.