Lambda School: Full Stack – Unit 2, Sprint 6: Composing React Components and Passing Data Via Props [1 of 2]

Sprint 6-2

JavaScript Modules

As we know, components are self-contained pieces of a program that perform a specific function. In React, components need to share state. This is often done using a helper object called props. JavaScript modules are the building blocks of React components. They’re individual files with their own code. Like closure and scope, they restrict the access of information, securing data from the rest of the given application. Code from a module can still be explicitly accessed by other modules, however, if they’re given permission to do so.

History

  1. In its infancy, inserting JavaScript into a web application was done via a <script> tag inside of an HTML file.
    1. These ran in sequence, from top to bottom.
    2. To reuse code, it had to be copied and pasted.
    3. Functions and variables were all in the global scope.
  2. The <src> attribute came along and added reusability, but was still sequentially executed and globally scoped.
  3. Sometime around 2006, developers used module patterns like IIFE (Immediately Invoked Function Expression, or “iffy”).
    1. These are JavaScript functions that run as soon as they’re defined.
    2. They provide privacy from global scope to inner variables.
  4. Node.JS was released in 2009, allowing JavaScript to run outside of the browser.
    1. CommonJS (a standard JS library) provided it with a standard for sending data to and from file systems.
    2. It relies on transpilers and 3rd-party bundlers like webpack, rollup and Babel, which all require downloading, configuring and updating.
  5. Today, JavaScript modules (ECMAScript modules) provide native functionality to import and export functions, components and data from files via the import and export keywords.
    1. No dependencies needed, its vanilla ES6

How To

Using modules in JavaScript (and by extension, React) is actually really simple. In a .js file with a function, component or data that you want to export, you choose what you want to export with the “export” keyword. Then, in the file that you want to use that function, component or data, you bring it in with the “import” keyword. That’s it.

Here are examples:

export const someFunction = () => {
  return something;
}

The above exports someFunction. It can be in any .js file, like “someFunction.js”.

import { someFunction } from './someFunction.js';

That line imports someFunction into whatever other .js file we place it in. Imports are usually done at the top of the file where the import is happening.

Maybe we have a file with more than one function in it that we want to export. We can do so like this:

export const somefunction = () => {return something};
export const someOtherFunction = () => {return another thing};
export const aThirdFunction = () => {return more stuff};

In the file with the functions we want to use, we export them all. Then, in the file in which we want to make use of those functions, we import them like this:

import { someFunction, someOtherFunction, aThirdFunction } from './someFunctions.js';

That’s the basics of exporting functions from one file and importing them into another. There’s one last type of import/export. Its called a “default” export.

  • “export” allows a single named function to be imported into another module via the “import” keyword
  • “export default” makes it so a given resource can be imported into another module by default and any other resources in that file need to be imported by name

Export default” can be done inline, like we did with the functions above, or at the end of the file (which is how I usually do it).

const exportMe = aVariable => {
  return some amazing stuff;
};

export default exportMe;

So, what does export default actually do on the importing file’s side? Basically, just this:

import { someFunction } from './someFunction.js';

That’s how we import a named function, normally.

import exportMe from './theFileItsIn.js';

So, the only real difference to my eyes, so far, is that we don’t need to put the function in curly braces to basically destructure it before import. I think its akin to something we’ve already used in projects:

import React from 'react';

And also:

import { useState, useEffect } from 'react';

React is probably exported by default from ‘react’ and the hooks are probably named exports, hence the curlies. Also, to my understanding, we’re doing this without a file path using ‘react’ the same way we would with other libraries like ‘axios’ or ‘styled-components’ because they’re installed libraries and are somehow tracked by the code editor or IDE. Its likely in an environment file or something.

While we’re on the topic of file paths, here are some examples of path prefixes:

  • current directory: ./
  • parent directory: ../
  • parent of parent directory: ../../

When importing from a file, we need to include the prefix, so that the file system can be navigated from a point-of-origin of the file that we’re loading to. So, for example, if you’re importing into a file called App.js and your component is in a folder, on the same level, called “components”, your path could look like this: “./components/filename.js”.

Props

Props are used to pass information held on state from inside of one component to another. They’re read-only – we don’t make changes to props data. This keeps the data-flow in an application from getting messy. It also reduces where data is changed in an application, so we know exactly which components are making changes and can troubleshoot with greater ease. Its a much smoother experience than hunting through an application with data changes in every file.

State and props management in components

Stateful components hold state data. This data can be objects placed inside of constructor functions (the traditional method) or function components using the .useState hook (the new method). In a React application, state is either loaded and stored in a centralized component dedicated to state management or a component that renders other components using that state. If data is consumed by multiple components, its best to centralize it in state in a top-level component common to the consuming components.

Components rendered by a stateful component can receive state as props. It can be sent using the props object to the consuming child component and looks like an HTML attribute. If changes need to be made to that data, the props data is not changed. Instead, updates are sent to the parent component that holds state, often using a called function. That state is then recirculated throughout the application.

Consider these parent, child and grandchild components:

const BlackSabbath = () => {
  const [member, setMember] = useState({name: 'Geezer Butler', role: 'bassist'});
  return <BandInfo member={member} />;
}
const BandInfo = (props) => {
  return (
    <div>
      <ShowMember member=(props.member) />
    </div>
  )
}
const ShowMember = props => {
  return (
    <div>
      <h2>{props.member.name} is the {props.member.role} for Black Sabbath.</h2>
    </div>
  )
}

So, the parent component places an object with two key-value pairs in state as an initial value. Because its using .useState() we know its a function component, not a class component. It then sets a named prop to that value in JSX where it calls a React component called BandInfo.

The “member” on the outside is the prop name. Its similar to an HTML/JSX attribute. The “member” inside of the curly braces is the value from state, so it has the object info. I’ve never liked it when the prop and the value its assigned use the same name. I find it confusing, but its a standard, because that data is referencing the same thing, even if the use is a little different for each (one is the data, the other is used to point to the data when it goes to another component).

BandInfo receives the props from its parent component as an object argument then sends it off as another named prop to its own child component, ShowMember. Note that because it was received as props, its now prefaced with “props.” and then the named prop.

ShowMember finally receives the props from BandInfo as an argument and returns a React element (JSX) in which it pulls out individual attributes from the object to render an <h2> onscreen. So, {user} is passed as {props.user} and then rendered with {props.user.name} and {props.user.role}. It uses props, the named props object, then the name of the attribute it needs to render.

Yeah, that seemed like a convoluted mess to me, too. It still does, to an extent, but I understand it a little more now. I see how adding some code to BlackSabbath to feed it an array of band members can now render the whole band. If we modify it further, we can even send a band name and an array of members and display other bands in their entirety. Any one of those components could get data from a different source and thus be used in different applications, too.

So that brings us to another question:

Why?

To answer this question, I’m going to quote directly from the Lambda text:

    • Control. As we learned with modules, when we break our components down into smaller functions, we gain greater control over what we display and how it works. By keeping state in one of a few select components and passing as props, we minimize the risk of making unintended changes to our state data.

    • Readability. Separate files are easy to keep in order, and make it easier for other developers to read through our code, know where our state is held, and where it’s being sent. The easier your code is to read and understand, the more likely someone is to ask you write more it for them.

    • Maintenance. If we want to make changes, we can find components quickly, and working in files that only manage one or two different aspects of our application is a much easier task than scrolling through hundreds of lines of code! Also, this way, we can isolate any problems that come up and debug faster.

    • Reusability. This is huge! Now we have reusable components, and they can render any data that we pass through, so long as it matches to type on our object. And with a few modifications, we render additional data if we added to our object. Or we could even pass in an array of hundreds of objects and render the information contained on each one. This would only require a few additional lines of code.

Ok. This is a lot again. I’m going to split this post into two halves, like my previous post. In the next, we’ll look at composing React components and passing props as dynamic data to a React component.

One thought on “Lambda School: Full Stack – Unit 2, Sprint 6: Composing React Components and Passing Data Via Props [1 of 2]

Leave a comment