DEV Community

Cover image for Bootcamping 02: Named exports and default exports - does it really matter?
Alef Lewitt
Alef Lewitt

Posted on

Bootcamping 02: Named exports and default exports - does it really matter?

INTRO
One of the cool parts of writing code files, is the abstraction of different components of logic, and ultimate cooporation of those components as one organic unit, one application.

One of the syntaxes characteristic of ES6 is the ability to export units of logic from one file, and import them in multiple other files, making functions, data - and in React, JSX components - reusable. Write them once, and use them in many instances.

NAMED DEFAULT
Now, sometimes it's typical to have a utilities.js file, or any file for that matter, where the developer stores a host of logic. A bunch of functions, or pieces of data. A classic example is an api.js file which exports the various CRUD actions.

export function getData() {
//fetch data by making a GET request
}

export function createData() {
//create data by making a POST request
}

export function updateData() {
//update data by making a PUT request
}

export function (deleteData) {
//delete data by making a DELETE request
}
Enter fullscreen mode Exit fullscreen mode

Then, in our React component for example, in order to use one of these functions, we import it using { }:

import {getData, createData} from "../../src/api.js"
Enter fullscreen mode Exit fullscreen mode

EXPORT DEFAULT
In other instances, such as when creating a separate file for each React component, it's common to export the component in a "default" manner. This means that the logic being exported is the only export from that file, period.

For example, in a React App:

const MyComponent = () => <div>Hello, World!</div>;
export default MyComponent;
Enter fullscreen mode Exit fullscreen mode

Here, when utilizing this component inside the parent component, there is no need for the { } in the import statement:

import MyComponent from "./MyComponent"
Enter fullscreen mode Exit fullscreen mode

Apparently, the lack or presence of { } around the import, let the application know if it can expect or allow additional imports from the file, or if this import will be the only one.

REDUX REDUCERS
Is this the only reason though, for including the { } when importing a named import? Thanks to one of my students' questions today, we discovered another reason, at least when it comes to Redux reducer functions. We discovered that by neglecting to place { } around a named import of a reducer function, actually "tricks" the code into expecting a regular funciton, instead of a special redux reducer function.

Let's take a step back and describe one unique ability of Redux reducer functions. Here's an example of a standard slice file in a redux store, where we've created a reducer function called updateItemQuantity.

export const CartSlice = createSlice({
  name: "cart",
  initialState,
  reducers: {
    updateItemQuantity: (
      state,
      action: PayloadAction<IAddItemToCartPayload>
    ) => {
      const item = state.items.find((item) => item.id === 
      action.payload.id);
      if (item) {
        item.quantity += action.payload.quantity;
      }

      state.total = state.items.reduce(
        (sum, item) => sum + item.price * item.quantity,
        0
      );
    },
  },
});
Enter fullscreen mode Exit fullscreen mode

Notice how in the reducer function declaration, we've written in 2 parameters - state and action.

However, surprisingly, when calling this function in a component, we only pass it ONE parameter, and Redux knows what to do with it!

  const handleAddToCart = (itemToAdd: IAddItemToCartPayload) => {
    dispatch(updateItemQuantity(itemToAdd));
  };
Enter fullscreen mode Exit fullscreen mode

Why is this?

The answer lies in the behind-the-scenes work that Redux does when we call dispatch. It automatically handles passing parameter 1 as the current state, and parameter 2 as an action object, and then takes the SINGLE parameter passed and injects it into the action object as a payload.

We discovered, that this all is well and good, as long as the export and import methods are consistent, as follows:
The reducer function is a named export:

export const { updateItemQuantity } = CartSlice.actions;
Enter fullscreen mode Exit fullscreen mode

and is also imported accordingly, complete with { }:

import updateItemQuantity from "../store/features/cartSlice";
Enter fullscreen mode Exit fullscreen mode

then what happens is as follows. Although the code editor recognizes the import, it displays an error that it expects 2 arguments for the function:

Image description

The error recorded is: Expected 2 arguments, but got 1.

The reason goes back to what we explained earlier - Redux knows how to handle the single parameter and inject it into the reducer function's second parameter (the action object). However, Redux only knows to do this if the exact reducer function exported is the same reducer function imported! When we left out the { } from the import statement, the code editor treated the updateItemQuantity as a regular javascript function and hence expected 2 parameters, just as there were 2 parameters when this (javascript) function was defined. However, only when we accurately imported it as the actual REDUX REDUCER function which was in-fact exported (by being precise with the { }) did the code editor recognize that this in-fact is NOT a regular JS function, but is rather a Redux reducer function, and hence is allowed to recieve only one parameter and leave the formulation of that parmater into the action object up to Redux to handle behind-the-scenes.

CONCLUSION

When you are learing how to code, you may find it tedious to stick to the details, like including curly parentheses versus leaving them out, but here's just another example of how a deeper understanding of what is going on behind the code, serves to help us appreciate the need for such attention to detail.

Happy learning!

Top comments (0)