Skip to main content

5 posts tagged with "trouble-shooting"

View All Tags

Exceeding Local Storage Capacity

· 3 min read
Hyunmo Ahn
Front End Engineer @ Line+

Introduction

Have you ever considered the capacity when using LocalStorage in your project?

Depending on the purpose of localStorage, it might not be something you often think about. However, if you're storing data that accumulates over time, the data can exceed the localStorage capacity.

What happens when it exceeds? 초과하면 과연 어떻게 동작할까요?

The answer is It throws an error.

Figure 1Local Storage Exceed Error

Solution

When the error occurs, the setStorage statement terminates due to the throw, and the error propagation begins.

If your project handles error propagation well, the error message will be logged, and if not, the project might stop.

To prevent this situation, you should use a try-catch block to catch the error and add logic to handle it.

const setStorage = (key, value) => {
try {
window.localStorage.setItem(key, value);
} catch (e) {
// Stop Throw Error
console.error('Local Storage Exceed Error', e);
}
}

Of course, an error indicates that the localStorage capacity has been exceeded, so the user will continue to encounter Local Storage Exceed Errors and won't be able to save data.

Therefore, in addition to error handling, you should also include logic to clear the data.

const setStorage = (key, value) => {
try {
window.localStorage.setItem(key, value);
} catch (e) {
// Stop Throw Error
console.error('Local Storage Exceed Error', e);
window.localStorage.clear();
}
}

Event Propagation in React

· 8 min read
Hyunmo Ahn
Front End Engineer @ Line+

Introduction

When mixing React's event handler method with vanilla Javascript's event handler method, event propagation may not work as intended. For example, in a structure like button1, button2, clicking button2 can trigger the event handler of button1 as well.

const buttonEl = document.getElementById('button1');
buttonEl.addEventListener('click', () => {
console.log('button1 clicked');
});

const handleClick = (e) => {
e.stopPropagation();
console.log('button2 clicked');
};

return (
<button id='button1'>
<button id='button2' onClick={handleClick}>Click me</button>
</button>
);
// When button2 is clicked
button2 clicked
button1 clicked

Solution

Event propagation in React differs from that in vanilla Javascript because React handles event propagation using a delegation method. (comment)

In React (post React 17), event listeners are registered on the rootDOM. Therefore, event listeners within React propagate as expected according to the DOM structure, but vanilla Javascript event propagation may not behave as anticipated.

While it's best to avoid mixing the two types of events, sometimes it's unavoidable when using third-party libraries or handling parts of the code you can't control. In such cases, you must block event propagation according to the behavior of each method.

// Unify event listener to vanilla Javascript
const buttonEl = document.getElementById('button1');
buttonEl.addEventListener('click', () => {
console.log('button1 clicked');
});

const button2El = document.getElementById('button2');
button2El.addEventListener('click', (e) => {
e.stopPropagation();
console.log('button2 clicked');
});

return (
<button id='button1'>
<button id='button2'>Click me</button>
</button>
);

// Or
// Unify event listener to React
const handleClick1 = (e) => {
console.log('button1 clicked');
};

const handleClick = (e) => {
e.stopPropagation();
console.log('button2 clicked');
};

return (
<button id='button1' onClick={handleClick1}>
<button id='button2' onClick={handleClick}>Click me</button>
</button>
);

Server Actions in Next.js are executed sequentially

· 5 min read
Hyunmo Ahn
Front End Engineer @ Line+

Introduction

In Next.js, server actions are used as a way to call functions via the server, even in a client environment. Server actions are executed sequentially, meaning that even if multiple functions are called, they are executed one at a time, so caution is needed when using them.

Behavior

Let's look at a brief example. There are two functions in this example.

'use server';
import { format } from 'date-fns';

export const callServerFn = async (count)=> {
return new Promise((resolve) => setTimeout(() => {
const time = format(new Date(), 'HH:mm:SS.sss');
console.log(`Server Call #${count}: `, time);
resolve(count)
}, 1000));
}

The callServerFn function takes 1 second to respond and is used as a server action with 'use server'. This logic is executed on the Next server. Logs are used to check the server response timing.

// client.tsx
'use client';

export const CallButton = () => {
const handleClick = async () => {
const result = await Promise.all([
callServer(1),
callServer(2),
callServer(3),
])
}

return (<button onClick={handleClick}>Hello World</button>);
}

The CallButton component calls the callServer function three times when the button is clicked and returns the result when all functions are completed.

Server Call #1:  01:23:22.010
Server Call #2: 01:23:24.011
Server Call #3: 01:23:25.012

When the button is pressed, three calls are made simultaneously, but the log results show that they are called sequentially, once every second.

How to implement line-spacing in CSS

· 2 min read
Hyunmo Ahn
Front End Engineer @ Line+

Introduction

In CSS, line-height is used to adjust line spacing. However, in a project I worked on, there was a requirement to support line-spacing. This article explains how to implement line-spacing using CSS.

Line Spacing

Line Gap

Line Gap

Line Gap

Line Spacing (5px)

Line Height

Line Gap

Line Gap

Line Gap

Line Height (fontSize + 5px)

Cheat Sheet

Unlike line-height, which applies padding both above and below the text, line-spacing applies padding only between lines. The difference here is that line-spacing does not add padding at the beginning and end of the text block.

You can implement line-spacing using margins.

const lineHeight = 20; // origin line-height
const lineSpacing = 10;
const margin = - (lineSpacing / 2);

const LineSpacingText = () => {
return (
<p style={{
marginTop: `${margin}px`,
marginBottom: `${margin}px`,
lineHeight: `${lineHeight + lineSpacing}px`,
}}>
Hello, World!
</p>
);
};

Playground

Line Spacing 0px

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet.

Line Spacing (0px)

Line Height (0px + fontSize)

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet.

Line Height (0px + fontSize)

Reasons to Avoid Nested Components

· 4 min read
Hyunmo Ahn
Front End Engineer @ Line+

Introduction

During a project, I encountered an issue where unnecessary re-renders were occurring.

The problem was not just rendering, but the fact that the DOM was being completely redrawn with each render. The culprit was the use of nested components in the code.

In this article, I will explain why you should avoid using nested components.

Cheat Sheet

// Bad
const List = ({ hasWrapper, borderStyle, children }) => {
const Border = () => {
return <p style={borderStyle}>{children}</p>;
}

if (hasWrapper) {
return (
<Wrapper>
<Border />
</Wrapper>
);
}

return <Border/>;
};
// Good
const List = ({ hasWrapper, borderStyle, children }) => {
if (hasWrapper) {
return (
<Wrapper>
<Border borderStyle={borderStyle}>
{children}
</Border>
</Wrapper>
);
}

return (
<Border borderStyle={borderStyle}>
{children}
</Border>
);
};

const Border = ({ borderStyle, children }) => {
return <p style={borderStyle}>{children}</p>;
}

As shown above, you might use a nested component to reuse the code for Border and the props of List. However, in such cases, the children are unnecessarily repaint in the DOM with each render.