KEMBAR78
Module 02 FSD | PDF | Constructor (Object Oriented Programming) | Programming
0% found this document useful (0 votes)
6 views50 pages

Module 02 FSD

Uploaded by

srajan kumar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views50 pages

Module 02 FSD

Uploaded by

srajan kumar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 50

FULL STACK DEVELOPMENT Srinivas V

Module-2

1
FULL STACK DEVELOPMENT Srinivas V

Module-2
React Component Issue tracker, React classes, Composing components, Passing
data using properties, Passing data using children, Dynamic composition.

React Components:
React components can be composed using other components and basic HTML elements; they can
respond to user input, change state, interact with other components, and much more.
Issue Tracker: GitHub Issues or Jira. These applications helps to create a bunch of issues or bugs,
assign them to people, and track their statuses. These are essentially CRUD applications (Create,
Read, Update, and Delete a record in a database) that manage a list of objects or entities.
In the case of the Issue Tracker, it only deal with a single object or record, because that’s good
enough to depict the pattern.
Issue Tracker: a list of issues.

The requirement list for the Issue Tracker application,


 The user should be able to view a list of issues, with an ability to filter the list by various
parameters.
 The user should be able to add new issues, by supplying the initial values of the issue’s fields.
 The user should be able to edit and update an issue by changing its field values.
 The user should be able delete an
issue. An issue should have following
attributes:
• A title that summarizes the issue (freeform long text)
• An owner to whom the issue is assigned (freeform short text)
• A status indicator (a list of possible status values)
• Creation date (a date, automatically assigned)

2
FULL STACK DEVELOPMENT Srinivas V

• Effort required to address the issue (number of days, a number)


• Estimated completion date or due date (a date, optional)

React Classes
React classes are used to create real components.
These classes can then be reused within other components, handle events, and so much more.
React classes are created by extending React.Component, the base class from which all custom
classes must be derived. Within the class definition, at the minimum, a render() method is
needed. This method is what React calls when it needs to display the component in the UI.
render() is one that must be present, otherwise the component will have no screen presence. The
render() function is supposed to return an element (which can be either a native HTML
element or an instance of another React component).
Example:
change the Hello World example from a simple element to use a React class called
HelloWorld, extended from React.Component:
...
class HelloWorld extends React.Component {
...
}
...
Now, within this class, a render() method is needed, which should return an element.
...
render() {
return (
<div title="Outer div">
<h1>{message}</h1>
</div>
);
...
Now move all the code for message construction to within the render() function so that it
remains encapsulated within the scope where it is needed rather than polluting the global
namespace.

3
FULL STACK DEVELOPMENT Srinivas V

...
render() {
const continents = ['Africa','America','Asia','Australia','Europe'];
const helloContinents = Array.from(continents, c => `Hello ${c}!`);
const message = helloContinents.join(' ');
return (
...
);
...

In React, class components are a fundamental way to create reusable UI elements. These classes are
built by extending React.Component, which provides essential functionality such as state
management and lifecycle methods.

Key Features of React Classes


1. Extends React.Component – Every class component must extend React.Component to
inherit its capabilities.
2. Render Method – The render() function is mandatory and must return a JSX element that
React will display in the UI.
3. State Management – Class components can hold and update state using this.state.
4. Lifecycle Methods – Special methods like componentDidMount(), componentDidUpdate(),
and componentWillUnmount() allow control over different stages of the component’s
existence.

4
FULL STACK DEVELOPMENT Srinivas V

HelloWorld and div are actually React component classes, whereas <HelloWorld /> and <div />
are tangible components or instances of the component class. Needless to say, there is only
one HelloWorld class, but many HelloWorld components can be instantiated based on this
class.

5
FULL STACK DEVELOPMENT Srinivas V

Composing Components
React allows building UI using both built-in and user-defined components. Component
composition helps break the UI into smaller, independent pieces, making it easier to develop,
understand, and maintain. A component takes inputs (called properties) and its output is the rendered
UI of the component.
Things to remember when composing components:
• Larger components should be split into fine-grained components when there is a
logical separation possible between the fine-grained components.
• When there is an opportunity for reuse, components can be built which take in
different inputs from different callers.
• React’s philosophy prefers component composition in preference to inheritance (eg. a
specialization of an existing component can be done by passing properties to the generic
component rather than inheriting from it).
• In general, remember to keep coupling between components to a minimum. (coupling
is where one component needs to know about the details of another component, including
parameters or properties passed between them).

It will have three parts:

 a filter to select which issues to display

 the list of issues

 finally an entry form for adding an issue.

Three placeholder classes—IssueFilter, IssueTable, and IssueAdd

6
FULL STACK DEVELOPMENT Srinivas V
(BIS601)\\
The IssueFilter component,

...

class IssueFilter extends React.Component

{ render() {

return (

<div>This is a placeholder for the issue filter.</div>

);

...

The other two classes,

...

class IssueTable extends React.Component {

...
<div>This is a placeholder for a table of issues.</div>

...
class IssueAdd extends React.Component {

...

<div>This is a placeholder for a form to add an issue.</div>

...
To put these together, remove the Hello World class and add a class called IssueList.

...

class IssueList extends React.Component {


}

...
7
FULL STACK DEVELOPMENT Srinivas V
(BIS601)\\
Now add a render() method. Within this method, add an instance of each of the new placeholder
classes separated by a <hr> or horizontal line.

The return of render() has to be a single element, these elements have to be enclosed within a <div>
or a React Fragment component. The Fragment component is like an enclosing <div> but it has no
effect on the DOM.

Use a Fragment component like this in the IssueList’s render() method:

...

render() {

return (

<React.Fragment>

<h1>Issue Tracker</h1>

<IssueFilter />

<hr />

<IssueTable />

<hr />

<IssueAdd />

</React.Fragment>

);

...
Finally, instead of instantiating a HelloWorld class, instantiate the IssueList class, which will
place under the contents div.
...
const element = <HelloWorld />;
const element = <IssueList />;

...
8
FULL STACK DEVELOPMENT Srinivas V

Listing 3-2. App.jsx: Composing Components (shows the new contents of the App.jsx file with all the
component classes.)

class IssueFilter extends React.Component

{ render() {

return (

<div>This is a placeholder for the issue filter.</div>

);

class IssueTable extends React.Component

{ render() {

return (

<div>This is a placeholder for a table of issues.</div>

);

class IssueAdd extends React.Component

{ render() {

return (

<div>This is a placeholder for a form to add an issue.</div>

);

class IssueList extends React.Component {

9
FULL STACK DEVELOPMENT Srinivas V

render() {

return (

<React.Fragment>

<h1>Issue Tracker</h1>

<IssueFilter />

<hr />

<IssueTable />

<hr />

<IssueAdd />

</React.Fragment>

);

const element = <IssueList />;

ReactDOM.render(element, document.getElementById('contents'));

The above figure shows the Issue Tracker by composing components.


Passing Data Using Properties:
10
FULL STACK DEVELOPMENT Srinivas V

Composing components without any variables is not so interesting. It should be possible to pass
different input data from a parent component to a child component and make it render differently on
different instances. In the Issue Tracker application, one such component that can be instantiated with
different inputs is a table-row showing an individual issue. Depending on the inputs (an issue), the
row can display different data.

The above figure shows the IssueList UI hierarchy with issue rows.

Create a component called IssueRow, and then use this multiple times within IssueTable, passing in different
data to show different issues:

...

class IssueTable extends React.Component {

render() {

return (

<table>

<thead>

<tr>

11
FULL STACK DEVELOPMENT Srinivas V

<th>Title</th>

</tr>

</thead>

<tbody>

<IssueRow /> {/* somehow pass Issue 1 data to this */}

<IssueRow /> {/* somehow pass Issue 2 data to this */}

</tbody>

</table>

);

...

The easiest way to pass data to child components is using an attribute when instantiating a
component. Any custom attribute can also be passed in a similar manner like this from IssueTable:

Used the name issue_title rather than simply title to avoid a confusion between this custom attribute
and the HTML title attribute. Now, within the render() method of the child, the attribute’s value can
be accessed via a special object variable called props, which is available via the this accessor. For
example, this is how the value of issue_title can be displayed within a cell in the IssueRow
component:

12
FULL STACK DEVELOPMENT Srinivas V

It passed across a simple string. Other data types and even JavaScript objects can be passed this way.
Any JavaScript expression can be passed along, by using curly braces ({}) instead of quotes, because
the curly braces switches into the JavaScript world.

So, pass the issue’s title (as a string), its ID (as a number), and the row style (as an object) from

IssueTable to IssueRow. Within the IssueRow class, use these passed-in properties to display the ID
and title and set the style of the row, by accessing these properties through this.props

The code for the complete IssueRow class:

13
FULL STACK DEVELOPMENT Srinivas V

Give the rows a silver border of one pixel and some padding, say four pixels. The style object that
would encapsulate this specification would be as follows:

This can be passed on to the IssueRow component using rowStyle={rowStyle} when instantiating it.
This, and the other variables, can be passed to IssueRow while instantiating it like this:

Note that not using string-like quotes for the Issue ID since it is a number or for rowStyle since it
is an object. Instead use the curly braces, which makes it a JavaScript expression.
Construct the IssueTable component with a header row and two columns (ID and title), and two hard-
coded IssueRow components.
Specify an inline style for the table to indicate a collapsed border and use the same rowStyle variable
to specify the header row styles, to make it look uniform.
Listing 3-4 shows the modified IssueTable component class

14
FULL STACK DEVELOPMENT Srinivas V

The above figure shows passing data to child components

15
FULL STACK DEVELOPMENT Srinivas V

Passing Data Using Children


 There is another way to pass data to other components, using the contents of the HTML-like
node of the component.
 In the child component, this can be accessed using a special field of this.props called
this.props.children.
 Just like in regular HTML, React components can be nested.

When the parent React component renders, the children are not automatically under it because the
structure of the parent React component needs to determine where exactly the children will appear.
So, React lets the parent component access the children element using this.props.children and lets the
parent component determine where it needs to be displayed. This works great when one needs to
wrap other components within a parent component.

...
class BorderWrap extends React.Component

{ render() {

const borderedStyle = {border: "1px solid silver", padding: 6};

return (

<div style={borderedStyle}>

{this.props.children}

</div>

);

}
...
Then, during the rendering, any component could be wrapped with a padded border like this:
...
<BorderWrap>

<ExampleComponent />

</BorderWrap>
16
FULL STACK DEVELOPMENT Srinivas V
...

17
FULL STACK DEVELOPMENT Srinivas V

Thus, instead of passing the issue title as a property to IssueRow, this technique could be used to
embed it as the child contents of <IssueRow> like this:

Now, within the render() method of IssueRow, instead of referring to this.props.issue_title, it will
need to be referred to as this.props.children, like this:

Let’s modify the application to use this method of passing data from IssueTable to IssueRow. Let’s
also pass in a nested title element as children, one that is a <div> and includes an emphasized piece
of text. This change is shown in Listing 3-5

Listing 3.5. App.jsx: Using Children Instead of Props

...

class IssueRow extends React.Component {

...

return (

<tr>

<td style={style}>{this.props.issue_id}</td>

<td style={style}>{this.props.issue_title}</td>

<td style={style}>{this.props.children}</td>

</tr>

);

...

}
18
FULL STACK DEVELOPMENT Srinivas V

The above figure shows passing data to child components

19
FULL STACK DEVELOPMENT Srinivas V

Dynamic Composition
 It will replace the hard-coded set of Issue Row components with a programmatically
generated set of components from an array of issues.
 Use a simple in-memory JavaScript array to store a list of issues.
 Now expand the scope of the issue from just an ID and a title to include as many fields of an
issue.
 It has just two issues. The field due is left undefined in the first record, to ensure that it handle
the fact that this is an optional field.
 The in-memory array declared globally at the beginning of the file App.jsx.

Listing 3-6. App.jsx: In-Memory Array of Issues


const issues = [
{
id: 1, status: New', owner: 'Ravan', effort: 5,
created: new Date('2018-08-15'), due: undefined,
title: 'Error in console when clicking Add',
},

{
id: 2, status: 'Assigned', owner: 'Eddie', effort: 14,
created: new Date('2018-08-16'), due: new Date('2018-08-30'),
title: 'Missing bottom border on panel',
},
];
Now modify the IssueTable class to use this array of issues rather than the hard-coded list.

20
FULL STACK DEVELOPMENT Srinivas V

Within the IssueTable class’ render() method, let’s iterate over the array of issues and generate an
array of IssueRows from it. The map() method of Array can map an issue object to an IssueRow
instance.

Also, instead of passing each field as a property, pass the issue object itself because there are many
fields as part of the object.

This is one way to do it, in-place within the table’s body:

To use a for loop instead of the map() method, can’t do that within the JSX, as JSX is not really a
templating language. It only can allow JavaScript expressions within the curly braces.

Have to create a variable in the render() method and use that in the JSX. Let’s create that variable for
the set of issue rows like that anyway for readability:
...
const issueRows = issues.map(issue => <IssueRow rowStyle={rowStyle} issue={issue}/>);
...
Now, replace the two hard-coded issue components inside IssueTable with this variable within
the <tbody> element like this:
...
...
<tbody>
{issueRows}
</tbody>

21
FULL STACK DEVELOPMENT Srinivas V

In other frameworks and templating languages, creating multiple elements using a template would
have required a special for loop construct (e.g., ng-repeat in AngularJS) within that templating
language.

The header row in the IssueTable class will now need to have one column for each of the issue fields,
so let’s do that as well.

But by now, specifying the style for each cell is becoming tedious, so let’s create a class for the table,
name it table-bordered, and use CSS to style the table and each table-cell instead. This style will need
to be part of index.html, and Listing 3-7 shows the changes to that file.

Now, remove rowStyle from all the table-cells and table-headers. One last thing that needs to be done
is to identify each instance of IssueRow with an attribute called key. The value of this key can be
anything, but it has to uniquely identify a row. React needs this key so that it can optimize the
calculation of differences when things change, for example, when a new row is inserted. Use the ID
of the issue as the key, as it uniquely identifies the row.

The final IssueTable class with a dynamically generated set of IssueRow components and the
modified header is shown in Listing 3-8.

22
FULL STACK DEVELOPMENT Srinivas V

The changes in IssueRow are quite simple. The inline styles have to be removed, and a few more
columns need to be added, one for each of the added fields. Since React does not automatically call
toString() on objects that are to be displayed, the dates have to be explicitly converted to strings. The
toString() method results in a long string, so let’s use toDateString() instead. Since the field due is
optional, need to also check for its presence before calling toDateString() on it. An easy way to do
this is to use the ternary ? - : operator in an expression like this:
...
issue.due ? issue.due.toDateString() : ''
...

23
FULLSTACK DEVELOPMENT | Srinivas V

After these changes, the screen look likes,

The above figure shows, Issue Rows constructed programmatically from an array.
BIT,MCA,Bangalore Page 1
FULLSTACK DEVELOPMENT | Srinivas V

React State

Introduction to React State

In React class components, state is a built-in object that stores data and determines how the
component renders and behaves. Here's a breakdown of how state works:

1. Static vs Dynamic Components


o Previously, we worked with static components that did not change.
o To make components responsive, React uses state.

2. What is State?
o State is a data structure in a React component that changes over time.
o Unlike props, which are immutable, state is mutable and affects the
UI.
3. State and Rendering
o The UI updates automatically when the state changes.
o React re-renders the component when the state is modified.

4. Goal of the Chapter


o Add a button that appends a new row to the issue list on click.
o Learn how to manipulate state, handle events, and pass data
between components.
5. Initial Implementation
o Start by adding a row using a timer instead of user interaction.
o Later, replace the timer with a button and a form for user input.

6. Defining State in a Component


o The state is stored in this.state
o as an object with key-value pairs.
o Example:

this.state = { issues: initialIssues };


o State should include only dynamic data affecting the UI.

BIT,MCA,Bangalore Page 2
FULLSTACK DEVELOPMENT | Srinivas V

7. Choosing What to Store in State


o Store data that affects rendering and changes dynamically.
o Example: The list of issues should be stored in the state.

BIT,MCA,Bangalore Page 3
FULLSTACK DEVELOPMENT | Srinivas V

o Do not store static values like table border styles.


8. Modifying State in IssueTable Component
o Store the initial issues in a variable called initialIssues.
o Update render() to use this.state.issues:

const issueRows = this.state.issues.map(issue =>


<IssueRow key={issue.id} issue={issue} />
);

9. Initializing State in Constructor


o The constructor sets the initial state:

class IssueTable extends React.Component


{ constructor() {

super();
this.state = { issues: initialIssues };
}
}

10. Next Steps

 Implement state updates using setState().


 Introduce user interaction with a button and form input

Initial State
o The state of a component is captured in a variable called this.state in
the component’s class, which should be an object consisting of one or
more key-value pairs, where each key is a state variable name and the
value is the current value of that variable.
o React does not specify what needs to go into the state, but it is
useful to store in the state anything that affects the rendered view
and can

BIT,MCA,Bangalore Page 4
FULLSTACK DEVELOPMENT | Srinivas V

change due to any event. These are typically events generated due to user interaction.

For now, let’s just use an array of issues as the one and only state of the component and use
that array to construct the table of issues.

Thus, in the render() method of IssueTable, let’s change the loop that creates the set of
IssueRows to use the state variable called issues rather than the global array like this

Note that we used only one state variable called issues.

We can have other state variables, for instance if we were showing the issue list in
multiple pages, and we wanted to also keep the page number currently being shown as
another state variable, we could have done that by adding another key to the object like
page: 0. The set of all changes to use the state to render the view of IssueTable is shown
in Listing 4-1

BIT,MCA,Bangalore Page 5
FULLSTACK DEVELOPMENT | Srinivas V

Async State Initialization

1. State Initialization in the Constructor

 React state must be assigned in the constructor.


 Since data is fetched asynchronously, initialize issues as an empty array:

constructor() {

BIT,MCA,Bangalore Page 6
FULLSTACK DEVELOPMENT | Srinivas V

super();
this.state = { issues: [] };

2. Modifying State with setState()

 State updates must be done using this.setState().


 Example of updating the issues state dynamically:

this.setState({ issues: newIssues });

 setState() merges the new state with the existing state.

3. Why Is an Async Call Needed for Fetching Data?

 Data is usually fetched from a server, not statically available.


 Fetching requires an asynchronous API call.
 We simulate this with setTimeout().

4. Simulating an API Call with setTimeout()

 Mimics real-world API call behavior:

loadData() {
setTimeout(() => {
this.setState({ issues: initialIssues });
}, 500);
}

 The 500ms delay represents an API response time.

5. Why Not Call loadData() in the Constructor?

 The constructor only initializes the component, but the UI rendering


happens later.
 Calling setState() inside the constructor may cause errors if rendering is not
finished.

BIT,MCA,Bangalore Page 7
FULLSTACK DEVELOPMENT | Srinivas V

6. React Lifecycle Methods for Managing State Updates

In ReactJS, every component creation process involves various lifecycle methods.


These lifecycle methods are termed as component’s life cycle.

These lifecycle methods are not complicated and called at various points during a
component’s life.

The lifecycle of the component is divided into four phases. They are:

1. Initial Phase
2. Mounting Phase
3. Updating Phase
4. Unmounting Phase

BIT,MCA,Bangalore Page 8
FULLSTACK DEVELOPMENT | Srinivas V

Each phase contains some lifecycle methods that are specific to the particular phase. Let
us discuss each of these phases one by one.

1.Initial Phase
It is the birth phase of the lifecycle of a ReactJS component. Here, the
component starts its journey on a way to the DOM.
In this phase, a component contains the default Props and initial State.
These default properties are done in the constructor of a component.
The initial phase only occurs once and consists of the following methods.

o getDefaultProps()
It is used to specify the default value of this.props.
It is invoked before the creation of the component or any props
from the parent is passed into it.
o getInitialState()
It id used to specify the default value of this.state.
It is invoked before the creation of the component.

2.Mounting Phase
In this phase, the instance of a component is created and inserted into the DOM.
It consists of the following methods:
o ComponentWillMount()
This is invoked immediately before a component gets rendered into the DOM .
In this case, when you call setState() inside this method, the component will not
re-render.
o ComponentDidMount()
This is invoked immediately after a component gets rendered and placed

BIT,MCA,Bangalore Page 9
FULLSTACK DEVELOPMENT | Srinivas V

on the DOM. Now, you can do any DOM querying operations.


o render()
This method is defined in each and every component.
It is responsible for returning a single root HTML node element.
If you don’t want to render anything, you can return a null or false value.

3.Updating Phase:
It is the next phase of the lifecycle of a react component. Here, we get new Props and
change State.
This phase also allows to handle user interaction and provide communication with the
components hierarchy.
The main aim of this phase is to ensure that the component displaying the latest version
of itself.
Unlike the Birth or Death phase, this phase repeats again and again.
This phase consists of the following methods:
o componentWillRecieveProps():
It is invoked when a component receives new props.
If you want to update the state in response to prop changes, you should
compare this.props and nextProps to perform state transition by using
this.setState() method.
o shouldComponentUpdate():
It is invoked when a component decides any changes/updation to the DOM.
It allows you to control the component’s behavior of updating itself.
If this method returns true, the component will update. Otherwise, the
component will skip the updating.
o componentWillUpdate()
It is invoked just before the component updating occurs.
Here, you can’t change the component state by invoking this.setState() method.

BIT,MCA,Bangalore Page 10
FULLSTACK DEVELOPMENT | Srinivas V

It will not be called, if shouldComponentUpdate() returns false.


o render()
It is invoked to examine this.props and this.state and return one of the following
types: React elements, Arrays and fragments, Booleans or null, String and Number.
If shouldComponentUpdate() returns false, the code inside render() will be invoked
again to ensure that the component displays itself properly.

4.Unmounting Phase

It is the final phase of the react component lifecycle.


It is called when a component instance is destroyed and unmounted from the DOM.
This phase contains only one method and is given below:
o componentWillUnmount():
This method is invoked immediately before a component is destroyed
and unmounted permanently.

It performs any necessary cleanup related task such as invalidating timers,


event listener, cancelling network requests, or cleaning up DOM elements.

7. Fetching Data in componentDidMount()

 Ensures the component is fully rendered before making an API call.


 Implementation:

componentDidMount() {
this.loadData();
}

BIT,MCA,Bangalore Page 11
FULLSTACK DEVELOPMENT | Srinivas V

8. Key Takeaways

 State must be initialized in the constructor but updated with setState().


 Data fetching is asynchronous and should be handled in lifecycle methods.
 Use componentDidMount() to safely fetch and update state.
 Lifecycle methods help manage state and optimize rendering.

BIT,MCA,Bangalore Page 12
FULLSTACK DEVELOPMENT | Srinivas V

If you refresh the browser (assuming you’re still running npm run watch and npm start on
two different consoles), you will find that the list of issues is displayed as it used to be in
the previous steps. But, you will also see that for a fraction of a second after the page is
loaded, the table is empty, as shown in Figure 4-1

BIT,MCA,Bangalore Page 13
FULLSTACK DEVELOPMENT | Srinivas V

Updating State

1. Changing a Portion of the State Instead of Overwriting It

 Instead of replacing the whole state, we update just part of it.


 Example: Adding a new issue to the existing issues array.

2. Creating a Method to Add a New Issue

 The method assigns an ID and creation date before adding the issue:

createIssue(issue) {
issue.id = this.state.issues.length + 1;
issue.created = new Date();
}
3. Directly Modifying State is NOT Allowed

 The state must be treated as immutable.


 These incorrect examples modify state directly:

this.state.issues.push(issue); // + Incorrect issues


= this.state.issues; issues.push(issue);
this.setState({ issues: issues }); // + Incorrect
4. Why Can't We Modify State Directly?

 React does not detect direct mutations of the state.


 Lifecycle methods that compare previous and new states may fail.
 Using setState() ensures React detects changes and rerenders correctly.

5. Correct Way: Using a Copy of the State

 Create a shallow copy of the array using slice():

const issues = this.state.issues.slice(); issues.push(issue);


this.setState({ issues: issues });

 This ensures React recognizes a new reference and updates the


component properly.

BIT,MCA,Bangalore Page 14
FULLSTACK DEVELOPMENT | Srinivas V

6. Alternative: Using Immutability Libraries

 Libraries like Immutable.js help manage deep state updates efficiently.


 Not needed for simple cases like appending an issue.

7. Simulating an Automatic Issue Addition

 Instead of a UI, we automatically add an issue after 2 seconds using


setTimeout().

8. Define a Sample Issue

 Declare a predefined issue:

const sampleIssue =
{ status: 'New', owner:
'Pieta',
title: 'Completion date should be optional',
};

9. Adding the Sample Issue with a Timer

 In constructor(), schedule an automatic addition of the issue:

setTimeout(() => {
this.createIssue(sampleIssue);
}, 2000);

10. Key Takeaways

Never modify state directly; always use setState().


Make a copy of the state before updating it.
Use lifecycle methods to manage state updates.
For complex state updates, consider using immutability libraries. State updates
trigger React rerenders, ensuring UI consistency.

This should automatically add the sample issue to the list of issues after the page is loaded.
The final set of changes—for using a timer to append a sample issue to the list of issues
—is shown in Listing 4-3

BIT,MCA,Bangalore Page 15
FULLSTACK DEVELOPMENT | Srinivas V

On running this set of changes and refreshing the browser, you’ll see that there are two rows of
issues to start with. After two seconds, a third row is added with a newly generated ID and the
contents of the sample issue. A screenshot of the three- row table is shown in Figure 4-2

BIT,MCA,Bangalore Page 16
FULLSTACK DEVELOPMENT | Srinivas V

Note that we did not explicitly call a setState() on the IssueRow components. React
automatically propagates any changes to child components that depend on the parent
component’s state. Further, we did not have to write any code for inserting a row into the DOM.
React calculated the changes to the virtual DOM and inserted a new row. At this point, the
hierarchy of the components and the data flow can be visually depicted, as shown in Figure 4-3.

BIT,MCA,Bangalore Page 17
FULLSTACK DEVELOPMENT | Srinivas V

Lifting State Up

In React, Lifting State Up is a pattern used to manage shared state between multiple
components. Instead of keeping state separately in sibling components, we move it to their
closest common parent. This allows child components to receive the shared state as props and
update it via callback functions provided by the parent.

Why Lift State Up?

 Ensures Sibling Communication: React does not allow direct


communication between sibling components. Instead, the parent can hold
the state and pass it down.
 Centralized State Management: Keeping state in a common parent
prevents data duplication and inconsistencies.
 Easier Updates: Since all state changes happen in the parent, updating and
debugging become simpler.

Example (Issue Tracker Scenario)

 Initially, IssueTable managed the issues list and createIssue() method.


 To allow IssueAdd to trigger issue creation, state and methods were
moved to IssueList (the common parent).
 IssueTable now receives issues as props instead of managing its own state.
 IssueAdd calls createIssue() through a prop function from IssueList.

Key Changes in Code

1. Move State and Methods to Parent (IssueList)

class IssueList extends React.Component { constructor() {


super();
this.state = { issues: [] };
this.createIssue = this.createIssue.bind(this);
}
createIssue(issue) {
issue.id = this.state.issues.length + 1; issue.created = new
Date();
const newIssueList = [...this.state.issues, issue];

BIT,MCA,Bangalore Page 18
FULLSTACK DEVELOPMENT | Srinivas V

this.setState({ issues: newIssueList });


}
render()
{ return (
<>
<IssueTable issues={this.state.issues} />
<IssueAdd createIssue={this.createIssue} />
</>
);
}
}

2. Pass State Down as Props (IssueTable)

<IssueTable issues={this.state.issues} />

3. Pass Method as Props (IssueAdd)

<IssueAdd createIssue={this.createIssue} />

4. Use the Passed Method in IssueAdd

setTimeout(() => { this.props.createIssue(sampleIssue);


}, 2000);

This setup allows IssueAdd to trigger a new issue addition without directly modifying the state.
Instead, it calls createIssue() in IssueList, ensuring state updates in a controlled way.

Event Handling
Let’s now add an issue interactively, on the click of a button rather than use a timer to do this.
We’ll create a form with two text inputs and use the values that the user enters in them to add a
new issue. An Add button will trigger the addition. Let’s

BIT,MCA,Bangalore Page 19
FULLSTACK DEVELOPMENT | Srinivas V

start by creating the form with two text inputs in the render() method of IssueAdd in place of
the placeholder div

BIT,MCA,Bangalore Page 20
FULLSTACK DEVELOPMENT | Srinivas V

At this point, clicking Add will submit the form and fetch the same screen again. That’s not
what we want. Firstly, we want it to call createIssue() using the values in the owner and title
fields. Secondly, we want to prevent the form from being submitted because we will handle the
event ourselves.

So, let’s rewrite the form declaration with a name and an on Submit handler like this.

Now, we can implement the method handleSubmit() in IssueAdd. This method receives the
event that triggered the submit as an argument.In order to prevent the form from being submitted
when the Add button is clicked, we need to call the preventDefault() function on the event.
After the call to createIssue(), let’s keep the form ready for the next set of inputs by clearing the
text input fields.

Since handleSubmit will be called from an event, the context, or this will be set to the object
generating the event, which is typically the window object. Since handleSubmit will be called
from an event, the context, or this will be set to the object generating the event, which is
typically the window object.

BIT,MCA,Bangalore Page 21
FULLSTACK DEVELOPMENT | Srinivas V

The new full code of the IssueAdd class, after these changes, is shown in Listing 4-7

You can now test the changes by entering some values in the owner and title fields and clicking
Add. You can add as

BIT,MCA,Bangalore Page 22
FULLSTACK DEVELOPMENT | Srinivas V

many rows as you like. If you add two issues, you’ll get a screen like the one in Figure 4-5.

At the end of all this, we have been able to encapsulate and initiate the creation of a new issue
from the IssueAdd component itself. This new UI hierarchy data and function flow is depicted
in Figure 4-6.

BIT,MCA,Bangalore Page 23
FULLSTACK DEVELOPMENT | Srinivas V

Stateless Components

Stateless components, also called functional components, are components that


only receive props and render UI without maintaining any internal state.

Why Use Stateless Components?

 Improved Performance: They are faster because they don’t have lifecycle
methods or state updates.
 Cleaner Code: They are simpler and easier to read.
 Better Maintainability: They focus only on rendering, making
debugging easier.

Example: Converting Class Components to Stateless Components Before (Class


Component)
class IssueRow extends React.Component { render() {
const issue = this.props.issue; return (
<tr>
<td>{issue.id}</td>
<td>{issue.status}</td>
<td>{issue.owner}</td>
<td>{issue.created.toDateString()}</td>
<td>{issue.effort}</td>
<td>{issue.due ? issue.due.toDateString() : ''}</td>
<td>{issue.title}</td>
</tr>
);
}
}
After (Stateless Functional Component)
const IssueRow = ({ issue }) => (
<tr>
<td>{issue.id}</td>
<td>{issue.status}</td>
<td>{issue.owner}</td>
<td>{issue.created.toDateString()}</td>
<td>{issue.effort}</td>

BIT,MCA,Bangalore Page 24
FULLSTACK DEVELOPMENT | Srinivas V

<td>{issue.due ? issue.due.toDateString() : ''}</td>


<td>{issue.title}</td>
</tr>
);

Designing Components
Designing Components Most beginners will have a bit of confusion between state and props,
when to use which, what granularity of components should one choose, and how to go about it
all. This section is devoted to discussing some principles and best practices.

State vs. Prop


Both state and props hold model information, but they are different. The props are immutable,
whereas state is not. Typically, state variables are passed down to child components as props
because the children don’t maintain or modify them. They take in a read-only copy and use it
only to render the view of the component. If any event in the child affects the parent’s state, the
child calls a method defined in the parent. Access to this method should have been explicitly
given by passing it as a callback via props.

Anything that can change due to an event anywhere in the component hierarchy qualifies as
being part of the state. Avoid keeping computed values in the state; instead, simply compute
them when needed, typically inside the render() method.

BIT,MCA,Bangalore Page 25
FULLSTACK DEVELOPMENT | Srinivas V

Component Hierarchy
 Split the application into components and subcomponents.
 Typically, this will reflect the data model itself. For example, in the Issue
Tracker, the issues array was represented by the IssueTable component,
and each issue was represented by the IssueRow component.
 Decide on the granularity just as you would for splitting functions and
objects. The component should be self-contained with minimal and logical
interfaces to the parent.
 If you find it doing too many things, just like in functions, it should
probably be split into multiple components, so that it follows the Single
Responsibility principle (that is, every component should be responsible
for one and only one thing).
 If you are passing in too many props to a component, it is an indication
that either the component needs to be split, or it need not exist: the parent
itself
 could do the job

Communication
 Communication between components depends on the direction.
Parents communicate to children via props; when state changes, the
props automatically change.
 Children communicate to parents via callbacks. Siblings and cousins can’t
communicate with each other, so if there is a need, the information has to
go up the hierarchy and then back down.
 This is called lifting the state up. This is what we did when we dealt
with adding a new issue. The IssueAdd component had to insert a row in
IssueTable.
 It was achieved by keeping the state in the least common ancestor,
IssueList. The addition was initiated by IssueAdd and a new array element
added in IssueList’s state via a callback.
 The result was seen in IssueTable by passing the issues array down as
props from IssueList.

BIT,MCA,Bangalore Page 26
FULLSTACK DEVELOPMENT | Srinivas V

 If there is a need to know the state of a child in a parent, you’re


probably doing it wrong.
 Although React does offer a way using refs, you shouldn’t feel the need if
you follow the one-way data flow strictly: state flows as props into
children, events cause state changes, which flows back as props.

Stateless Components
 In a well-designed application, most components would be stateless
functions of their properties. All states would be captured in a few
components at the top of the hierarchy, from where the props of all the
descendants are derived.
 We did just that with the IssueList, where we kept the state. We
converted all descendent components to stateless components, relying
only on props passed down the hierarchy to render themselves.
 We kept the state in IssueList because that was the least
common component above all the descendants that depended on
that state.
 Sometimes, you may find that there is no logical common ancestor. In
such cases, you may have to invent a new component just to hold the state,
even though visually the component has nothing

BIT,MCA,Bangalore Page 27

You might also like