Bootcamping 02: Named exports and default exports - does it really matter?
Alef Lewitt
Posted on November 24, 2024
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
}
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"
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;
Here, when utilizing this component inside the parent component, there is no need for the { }
in the import statement:
import MyComponent from "./MyComponent"
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 neglecting to place { }
around a named import of a reducer function, serves to effectively "trick" the code into expecting a regular function, 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
);
},
},
});
Notice how in the reducer function declaration, we've entered 2 parameters - state
and action
.
However, when calling this function in a component, we only pass it ONE parameter, and surprisingly Redux knows what to do with it!
const handleAddToCart = (itemToAdd: IAddItemToCartPayload) => {
dispatch(updateItemQuantity(itemToAdd));
};
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;
and is also imported accordingly, complete with { }
:
import { updateItemQuantity } from "../store/features/cartSlice";
However, if the curly parentheses are left out, like such:
import updateItemQuantity from "../store/features/cartSlice";
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:
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!
Posted on November 24, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.