Concepts of React — Part 3

Image for post
Image for post

📄 Table of Contents

▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬ ✦ ✦ ✦ ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬

◉ Controlled Component vs UNcontrolled Component

In most cases, we recommend using controlled components to implement forms. In a controlled component, form data is handled by a React component.

The alternative is UN-controlled components, where form data is handled by the DOM itself.

To write an UN-controlled component, instead of writing an event handler for every state update, you can use a ref to get form values from the DOM.

Since an UNcontrolled component keeps the source of truth in the DOM, it is sometimes easier to integrate React and non-React code when using UNcontrolled components. It can also be slightly less code if you want to be quick and dirty. Otherwise, you should usually use controlled components.

Default Values

In the React rendering lifecycle, the value attribute on form elements will override the value in the DOM.

With an UNcontrolled component, you often want React to specify the initial value, but leave subsequent updates uncontrolled. To handle this case, you can specify a defaultValue attribute instead of value.

In React, an <input type=”file” /> is always an UNcontrolled component because its value can only be set by a user, and not programmatically.

◉ Stateless Component vs Pure Component

In broader sense,

Higher Order Component(HOC) focusses on reusability of existing component.

Pure Components have performance benefits.

STATELESS COMPONENT declared as a function that has no state, do not have the component lifecycle methods and returns the same markup given the same props.

A quote from the React documentation:

PURE COMPONENT is one of the most significant ways to optimize React applications. The usage of Pure Component gives a considerable increase in performance because it reduces the number of render operation in the application.

Let’s look at the performance aspect of both.

//Pure component class Welcome extends React.PureComponent {  
render() {
return <h1>Welcome</h1>
}
}
// Stateless componentHello = () => {
return <h1>Hello</h1>;
}

So above there is an example of a very simple Welcome Pure Component and Hello Stateless Component. When you use these two in your Parent Component, you will see Hello will re-render whenever Parent Component will re-render but Welcome Component will not.

This is because PureComponent changes the life-cycle method shouldComponentUpdate and adds some logic to automatically check whether a re-render is required for the component. This allows a PureComponent to call the method render only if it detects changes in state or props.

When to use Pure Components?

Suppose you creating a dictionary page in which you display the meaning of all the English words starting with A. Now you can write a component which takes a word and its meaning as props and return a proper view. And suppose you using pagination to display only 10 words at a time and on scroll asking for another 10 words and updating the state of the parent component. Pure Components should be used in this case as it will avoid rendering of all the words which rendered in previous API request.

Also in cases where you want to use lifecycle methods of Component then we have to use Pure Components as stateless components don't have lifecycle methods.

When to use Stateless Components?

Suppose you want to create a label with some beautiful UI which will be used to rate the credibility of a profile like BEGINNER, MODERATE, EXPERT. Since its a very small component whose re-render will hardly make any difference and creating a new component for such a small case will be time-consuming. Also if you keep making components for very small-small view, soon you will end up with so many components and it will be hard to manage when working with a big project. Also always keep in mind Pure Component comes with peculiarities of the shallowEqual.

Conclusion

Pure Components gives a considerable increase in performance because it reduces the number of render operation in the application which is a huge win for complex UI and therefore advised to use if possible. Also, there will be cases where you want to use the lifecycle methods of Component and in such cases, we cannot use stateless components.

Stateless Components are easy and fast to implement. They are good for very small UI view where re-render cost won’t matter that much. They provide cleaner code and less number of files to deal with.

◉ React Context vs React Redux

If you’re only using Redux to avoid passing down props, context could replace Redux — but then you probably didn’t need Redux in the first place.

Context also doesn’t give you anything like the Redux DevTools, the ability to trace your state updates, middleware to add centralized application logic, and other powerful capabilities that Redux enables.

React Redux uses context internally but it doesn’t expose this fact in the public API. So you should feel much safer using context via React Redux than directly because if it changes, the burden of updating the code will be on React Redux and not you.

Applications which use centralised data and handle API request in Action creators using redux-thunk or redux-saga still would need redux. Apart from this redux has other libraries associated like redux-persist which allow you to save store data in localStorage and rehydrate on refresh which is what context API still doesn't support.

◉ e.preventDefault() vs e.stopPropagation vs return false

React uses synthetic events to handle events from button, input and form elements.

A synthetic event is a shell around the native DOM event with additional information for React. Sometimes you have to use event.preventDefault(); in your application.

🔘 event.preventDefault()

Prevents the browsers default behaviour (such as opening a link), but does not stop the event from bubbling up the DOM.

Example:-

Being an a tag, however, it also has a default behaviour — this being to navigate the browser to the location in the href attribute. In this instance we have this set to #, which in most browsers will just cause the page to jump back to the top.

Jumping back to the top of the page is not really our desired behaviour, so we can prevent this by using the preventDefault method. This prevents the default behaviour of an element.

In this case, a preventDefault is called on the event when submitting the form to prevent a browser reload/refresh.

🔘 event.stopPropagation()

Prevents the event from bubbling up the DOM, but does not stop the browsers default behaviour.

🔘 return false

Usually seen in jQuery code, it Prevents the browsers default behaviour, Prevents the event from bubbling up the DOM, and immediately Returns from any callback.

In vanilla JavaScript, returning false doesn’t have any effect on the default behaviour or event propagation of the element, as we can see here, it acts exactly as it did at the start.

It calls the click event on the button, also navigating to it’s href value, then bubbles up the DOM, calling the click event on the dropzone too.

However…

…in the context of jQuery, returning false will immediately exit the event listeners callback. This has the effect of both:

  • Preventing the default behaviour — navigating the browser to the a tag’s href attribute.
  • Stopping any event propagation — stopping the click event from bubbling up the DOM.

Un-normalized data

I had data from an API that had deeply nested data. I simply added this data to a Redux store without normalizing the data. This lead to a lot of code bloat and performance issues.

What if we want to change the information of a user?

If you simply mutate one of the posts, the component will not update with the user’s new information. You have to deep clone all the posts data or use immutable data structures. Even with ImmutableJS, changing deep structures can get messy:

You’ve seen that a lot of our logic has been looking up items by their ID field. Since we’ve been storing our data in arrays, that means we have to loop over all the items in the array using array.find() until we find the item with the ID we're looking for.

Realistically, this doesn’t take very long, but if we had arrays with hundreds or thousands of items inside, looking through the entire array to find one item becomes wasted effort. What we need is a way to look up a single item based on its ID, directly, without having to check all the other items. This process is known as “normalization”.

Normalizing

We can pull out the author and each liker as a user object into a map of all users. Then, replace each with a reference to the user, instead of the object.

Benefits

Now it is easy to update a user using this data. You can update the users without modifying any of the posts.

Normalized State Structure

“Normalized state” means that:

  • We only have one copy of each particular piece of data in our state, so there’s no duplication
  • Data that has been normalized is kept in a lookup table, where the item IDs are the keys, and the items themselves are the values.
  • There may also be an array of all of the IDs for a particular item type

JavaScript objects can be used as lookup tables, similar to “maps” or “dictionaries” in other languages. Here’s what the normalized state for a group of user objects might look like:

Image for post
Image for post

This makes it easy to find a particular user object by its ID, without having to loop through all the other user objects in an array:

Image for post
Image for post

You could use a library such as normalizr to normalize data for you based on a schema. This is great if you have little control over your data, such as if you are using an external API.

For more read @ https://medium.com/front-end-weekly/why-you-need-to-normalize-redux-data-b696b47aa39

Redux Selectors

A “selector” is simply a function that accepts Redux state as an argument and returns data that is derived from that state.

The use of selectors in your application can also provide performance optimizations.

Why should you use a selector?

It is a best practice to

  • keep your Redux store state minimal and
  • derive data from the state as needed.

Selectors help with that. They can compute derived data, allowing Redux to store the minimal possible state. Selectors are also very efficient. A selector is NOT recomputed unless one of its arguments changes.

It’s generally suggested that selectors are defined alongside reducers and exported, and then reused elsewhere (such as in mapStateToProps functions, in async action creators, or sagas) to colocate all the code that knows about the actual shape of the state tree in the reducer files.

We have discussed the merits of selectors enough, let’s finally dive into a few examples! Below is a simple selector function for retrieving a list of users from the store:

selectUsers = state => state.users;

We can make the above selector slightly more complex by getting user IDs:

selectUserIds = state => state.users.map(user => user.id);

And just to drive the point home, below is an even more complex selector to select users with a provided name:

selectUserIdsOfName = (state, name) => state.users.filter(user => user.name === name);

Best practices…

It is a best practice to give your selector function a name that contains “selector”. I prefer to use ‘select’ as a prefix, but it’s also common to use ‘Selector’ as a suffix (i.e. userIdsSelector instead of selectUserIds).

It is also common practice to keep your selector near the appropriate reducer. That way, if you change the shape of the state, it is easy to remember to change your selector as well.

Below is an example of a selector bundled with a reducer. We’ll use the reducer example from the official Redux docs:

// Reducer
function todoApp(state = [], action) {
switch (action.type) {
case GET_ALL_TODOS:
return {
todos: action.data.todos
};
default:
return state;
}
}// Selector
function getIncompleteTodos(state) {
return state.todos.filter((todo) => {
return !todo.completed
});
}

Now, instead of using something like the following in a smart component’s mapStateToProps() function:

function mapStateToProps(state) {
return {
incompleteTodos: state.todos.filter((todo) => {
return !todo.completed
});
}
}

We could use the newly created selector in its place:

function mapStateToProps(state) {
return {
incompleteTodos: getIncompleteTodos(state)
};
}
  • The code is more concise and we have made a big step toward encapsulating our state tree.
  • The component no longer cares how todos are stored in the state tree and it will be much easier to refactor if the state tree needs to be updated!

Memoization

Memoization is a form of caching. It involves tracking inputs to a function, and storing the inputs and the results for later reference. If a function is called with the same inputs as before, the function can skip doing the actual work, and return the same result it generated the last time it received those input values.

The official Redux docs briefly discuss memoization. There is also an amazing Redux middleware library for creating memoized selectors called Reselect.

Using Reselect selectors

Using reselect optimizes our Redux state management. As Redux practices immutability that means new object references will be created every time on action dispatch. This will hamper performance because re-render will be triggered on components even when the object references change but the fields haven’t changed.

Reselect library encapsulates the Redux state and checks the fields of the state and tells React when to render or not if the fields haven’t changed.

So, reselect saves precious time by shallowly traversing through the prev and current Redux states fields to check if they have changed despite having different memory references. If the fields have changed it tells React to re-render, if none fields have changed it cancels the re-render despite new state object created.

Experience with Front-end Technologies and MERN / MEAN Stack. Working on all Major UI Frameworks like React, Angular.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store