React Course: Preparing for Project Deployment [Diary App]

Hello, everyone! In this course, we will take a detailed look at the preparation process for deploying a diary app created with React. Deploying a web application developed with React requires several steps beyond simply uploading files to the server. In this article, we will cover pre-deployment checks and optimization, basic deployment methods, and post-deployment management and maintenance.

1. Project Optimization

Before deploying, it’s important to optimize the application. Optimization helps improve user experience, reduce load times, and save server costs. Here are some points to consider during the optimization process.

1.1. Code Splitting

In React, you can reduce initial load times through code splitting. Code splitting allows you to divide different parts of the application into separate chunks and load them only when needed. This provides faster responsiveness when users are using the application. You can use React.lazy and Suspense for this.


    // Example code
    const LazyComponent = React.lazy(() => import('./LazyComponent'));

    function App() {
        return (
            Loading...
}> ); }

1.2. Creating a Production Build

Running the application in development mode can degrade performance due to various development tools being activated. Therefore, you need to create a build in production mode. To generate a production build for a React app, use the following command.

npm run build

Executing this command will generate optimized files in the build folder.

2. Introduction to Deployment Methods

Now, let’s look at the deployment methods. There are various deployment platforms, but here we will introduce two representative methods: GitHub Pages and Netlify.

2.1. GitHub Pages

GitHub Pages is a free service for hosting static websites. The process for deploying a React application to GitHub Pages is as follows.

  1. Create a GitHub Repository: Create a new repository on GitHub.
  2. Install npm gh-pages: Install the gh-pages package for deployment to GitHub Pages.
  3. npm install --save gh-pages
  4. Edit package.json: Add the homepage property to the package.json file.
  5. 
            {
                "homepage": "https://username.github.io/repository-name"
            }
            
  6. Add Deployment Script: Add a deployment script to the scripts section of package.json.
  7. 
            "scripts": {
                "predeploy": "npm run build",
                "deploy": "gh-pages -d build"
            }
            
  8. Execute Deployment: Run the following command to deploy the application.
  9. npm run deploy

Now, you can access the deployed application via the {homepage} URL in your browser.

2.2. Netlify

Netlify is a very popular platform for hosting static websites. Here’s how to deploy a React application to Netlify.

  1. Create a Netlify Account: Visit the Netlify website to create a free account.
  2. Create New Site: Click the New site from Git button to connect to your GitHub repository.
  3. Build Settings: Set the build command to npm run build and specify the deployment directory as build.
  4. Deploy Site: Click the Deploy site button to start the deployment.

Once the deployment is complete, you can access the application using the URL provided by Netlify.

3. Post-Deployment Management and Maintenance

After deployment, it is important to check whether the application is functioning reliably. Here are some aspects to manage post-deployment.

3.1. Error Monitoring

It’s a good idea to use error tracking tools to monitor potential errors in the deployed application. Tools like Sentry allow you to track errors in real-time and resolve issues.

3.2. Performance Monitoring

Use tools like Google Analytics to monitor the performance of your application. This helps you understand user traffic and behavior, and collect data needed for product improvement.

3.3. Collecting User Feedback

After deploying the application, it’s important to collect feedback from users. Gather various insights from users about what they think of the application and use this information to improve the app.

4. Conclusion

Today, we examined the key steps in the preparation process for deploying a diary app made with React. We discussed code optimization, deployment methods, and post-deployment management and maintenance. I hope this article helps you with your React app deployment. Don’t forget to continuously improve and manage the application after deployment!

5. Additional Resources

Here are some additional resources related to React and application deployment:

React Course: [Diary App] Deployment

React is currently one of the most popular front-end libraries, offering a variety of features that make it easy to build complex user interfaces. In this tutorial, we will explore how to create a simple diary app using React and how to actually deploy it.

1. Project Overview

First, let’s take a look at the basic features of the diary app. This app will allow users to write, edit, and delete their diaries and view the diaries they have written in a list format. The basic UI elements are as follows:

  • Diary list display
  • Diary writing form
  • Diary detail view
  • Edit / delete diary functionality

2. Environment Setup

We will use Create React App to set up the React development environment. Open the terminal and enter the command below to create a new project:

npx create-react-app diary-app

After moving to the project directory, we will install the necessary packages:

cd diary-app
npm install axios react-router-dom

Here, axios is a library for making API calls, and react-router-dom is a library for navigation between pages.

3. Basic Structure Design

The basic structure of the diary app is designed as follows:


src/
|-- components/
|   |-- DiaryList.js
|   |-- DiaryForm.js
|   |-- DiaryDetail.js
|-- App.js
|-- index.js

The roles of each component are as follows:

  • DiaryList.js: The component that shows the list of diaries
  • DiaryForm.js: The form for writing a new diary
  • DiaryDetail.js: The page that shows the details of the selected diary

4. Component Implementation

4.1 DiaryList.js

Let’s implement the component that displays the list of diaries. It fetches data from the API and displays it in a list format:


import React, { useEffect, useState } from 'react';
import axios from 'axios';

const DiaryList = () => {
    const [diaries, setDiaries] = useState([]);

    useEffect(() => {
        const fetchDiaries = async () => {
            const response = await axios.get('/api/diaries');
            setDiaries(response.data);
        };
        fetchDiaries();
    }, []);

    return (
        

My Diary List

    {diaries.map((diary) => (
  • {diary.title}
  • ))}
); }; export default DiaryList;

4.2 DiaryForm.js

Let’s create a form for writing diaries:


import React, { useState } from 'react';
import axios from 'axios';

const DiaryForm = () => {
    const [title, setTitle] = useState('');
    const [content, setContent] = useState('');

    const handleSubmit = async (e) => {
        e.preventDefault();
        const newDiary = { title, content };
        await axios.post('/api/diaries', newDiary);
        // reset state
        setTitle('');
        setContent('');
    };

    return (
        
setTitle(e.target.value)} />
); }; export default DiaryForm;

4.3 DiaryDetail.js

Let’s implement the diary detail view page:


import React, { useEffect, useState } from 'react';
import axios from 'axios';

const DiaryDetail = ({ match }) => {
    const [diary, setDiary] = useState({});

    useEffect(() => {
        const fetchDiary = async () => {
            const response = await axios.get(`/api/diaries/${match.params.id}`);
            setDiary(response.data);
        };
        fetchDiary();
    }, [match.params.id]);

    return (
        

{diary.title}

{diary.content}

); }; export default DiaryDetail;

5. Routing Setup

Now we will set up routing between pages using react-router-dom:


import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import DiaryList from './components/DiaryList';
import DiaryForm from './components/DiaryForm';
import DiaryDetail from './components/DiaryDetail';

const App = () => {
    return (
        
            
                
                
                
            
        
    );
};

export default App;

6. Backend Setup

Before deploying the diary app, we need to set up the backend. We will create a simple RESTful API using Express.js. Create a new directory with the command below and install Express:

mkdir diary-backend && cd diary-backend
npm init -y
npm install express body-parser cors

Now, create the server.js file and add the code below:


const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');

const app = express();
const PORT = 5000;

app.use(cors());
app.use(bodyParser.json());

let diaries = [];

app.get('/api/diaries', (req, res) => {
    res.json(diaries);
});

app.post('/api/diaries', (req, res) => {
    const newDiary = { id: diaries.length + 1, ...req.body };
    diaries.push(newDiary);
    res.json(newDiary);
});

app.get('/api/diaries/:id', (req, res) => {
    const diary = diaries.find(d => d.id === parseInt(req.params.id));
    res.json(diary);
});

app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}.`);
});

7. Preparing for Deployment

Before deploying the app, we need to perform a few tasks. We will build the client and server to get them ready to run in a production environment.

npm run build

Now the client build has been generated in the build/ directory. We need to configure this directory’s contents to be served by the backend server.

8. Deployment

You can deploy the app using a cloud hosting service. For example, you can use Heroku.

heroku create diary-app
git add .
git commit -m "Deploying diary app"
git push heroku master

9. Conclusion

In this way, we have learned how to build and deploy a diary app using React and Express. Throughout this process, we were able to learn everything from the basic concepts of React to API integration and deployment. Now, try to build your own diary app!

10. Additional Resources

React Course: To Do App Upgrade Using useReducer

React has become an important tool in modern web development. In particular, while there are various methods for state management, useReducer is a React hook that helps effectively manage more complex state logic. In this tutorial, we will specifically cover how to utilize useReducer using a basic To Do app.

1. Project Structure

First, let’s look at the basic structure of the project. Typically, the structure of a To Do app is organized as follows:


/to-do-app
|-- /src
|   |-- /components
|   |   |-- TodoItem.js
|   |   |-- TodoList.js
|   |   └-- TodoForm.js
|   |
|   |-- App.js
|   └-- index.js
|-- package.json
|-- README.md
        

In the above structure, TodoItem represents an individual task, and TodoList shows the entire list of tasks. New tasks are added using TodoForm.

2. Introduction to useReducer

useReducer is useful when dealing with complex state, and it can be used in the following way:


const [state, dispatch] = useReducer(reducer, initialState);
        

state: Represents the current state, and dispatch: A function for state updates. reducer is a function that defines how the state should change.

3. Implementing a Basic To Do App

First, let’s implement a basic To Do app. The code below is a simple React app that allows users to add tasks and display the list.


import React, { useState } from 'react';

const App = () => {
    const [todos, setTodos] = useState([]);
    const [input, setInput] = useState('');

    const addTodo = () => {
        if (input.trim() !== '') {
            setTodos([...todos, { text: input, completed: false }]);
            setInput('');
        }
    };

    const toggleTodo = (index) => {
        setTodos(todos.map((todo, i) => 
            i === index ? { ...todo, completed: !todo.completed } : todo
        ));
    };

    return (
        

My To Do List

setInput(e.target.value)} placeholder="Enter a task" />
    {todos.map((todo, index) => (
  • toggleTodo(index)} style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}> {todo.text}
  • ))}
); }; export default App;

4. Managing State with useReducer

Now, let’s improve the above code using useReducer. When it is necessary to manage state in a complex manner, useReducer can be more useful.


import React, { useReducer } from 'react';

const initialState = { todos: [] };

const reducer = (state, action) => {
    switch (action.type) {
        case 'ADD_TODO':
            return { 
                ...state, 
                todos: [...state.todos, { text: action.payload, completed: false }] 
            };
        case 'TOGGLE_TODO':
            return {
                ...state,
                todos: state.todos.map((todo, index) =>
                    index === action.payload ? { ...todo, completed: !todo.completed } : todo
                ),
            };
        default:
            return state;
    }
};

const App = () => {
    const [state, dispatch] = useReducer(reducer, initialState);
    const [input, setInput] = React.useState('');

    const addTodo = () => {
        if (input.trim() !== '') {
            dispatch({ type: 'ADD_TODO', payload: input });
            setInput('');
        }
    };

    return (
        

My To Do List (useReducer)

setInput(e.target.value)} placeholder="Enter a task" />
    {state.todos.map((todo, index) => (
  • dispatch({ type: 'TOGGLE_TODO', payload: index })} style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}> {todo.text}
  • ))}
); }; export default App;

5. Advantages of useReducer

Using useReducer has the following advantages:

  • Complex state logic: It can maintain complex state logic clearly by managing multiple state transitions as independent actions.
  • Centralized management: When there are multiple state updates, all state changes can be handled by a single reducer function.
  • Performance improvement: It makes maintaining immutability easier, helping with performance optimization.

6. Component Separation

Now, let’s break the App component into smaller components to improve code readability. We will create TodoList and TodoForm components.


const TodoList = ({ todos, toggleTodo }) => (
    
    {todos.map((todo, index) => (
  • toggleTodo(index)} style={{ textDecoration: todo.completed ? 'line-through' : 'none' }} > {todo.text}
  • ))}
); const TodoForm = ({ addTodo, input, setInput }) => (
setInput(e.target.value)} placeholder="Enter a task" />
);

7. Final Code


import React, { useReducer, useState } from 'react';

const initialState = { todos: [] };

const reducer = (state, action) => {
    switch (action.type) {
        case 'ADD_TODO':
            return { 
                ...state, 
                todos: [...state.todos, { text: action.payload, completed: false }] 
            };
        case 'TOGGLE_TODO':
            return {
                ...state,
                todos: state.todos.map((todo, index) =>
                    index === action.payload ? { ...todo, completed: !todo.completed } : todo
                ),
            };
        default:
            return state;
    }
};

const TodoList = ({ todos, toggleTodo }) => (
    
    {todos.map((todo, index) => (
  • toggleTodo(index)} style={{ textDecoration: todo.completed ? 'line-through' : 'none' }} > {todo.text}
  • ))}
); const TodoForm = ({ addTodo, input, setInput }) => (
setInput(e.target.value)} placeholder="Enter a task" />
); const App = () => { const [state, dispatch] = useReducer(reducer, initialState); const [input, setInput] = useState(''); const addTodo = () => { if (input.trim() !== '') { dispatch({ type: 'ADD_TODO', payload: input }); setInput(''); } }; return (

My To Do List (useReducer)

dispatch({ type: 'TOGGLE_TODO', payload: index })} />
); }; export default App;

8. Conclusion

In this tutorial, we learned how to apply useReducer through the To Do app. useReducer helps manage complex state logic concisely, enhancing readability and maintainability. Furthermore, we were able to improve the structure of the code by breaking it into several components. This pattern can also be effectively used in more complex applications. In the next session, we will explore global state management using useContext.

React Course: Understanding useReducer

React is a component-based JavaScript library widely used for building user interfaces (UI). The two main state management hooks in React are useState and useReducer. In this tutorial, we will take a deep dive into useReducer.

1. What is useReducer?

useReducer is one of React’s built-in hooks used to manage complex state logic. This hook updates the state by calling a specific function whenever there is a state change. While useState is suitable for simple state management, useReducer is better suited for more complex logic or when multiple state changes are needed.

2. Basic Usage of useReducer

The basic way to use useReducer is as follows.


import React, { useReducer } from 'react';

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
    </div>
  );
}

As seen in the example above, useReducer uses two arguments:
the reducer function and the initial state. The reducer function takes the current state and action as parameters and returns the new state. The dispatch function updates the state using the action object.

3. Advantages of useReducer

  • Complex state management: useReducer is a powerful tool for managing multiple states. It allows for clear delineation of various actions and states.
  • Refactoring and scalability: Because the reducer function is maintained independently, it makes refactoring state logic easier.
  • Testability: The reducer function is a pure function, making it relatively easy to unit test.

4. Comparison of useReducer and useState

While both useState and useReducer help manage states, each is suited for different situations based on its usage.

Feature useState useReducer
Simple state Advantageous Disadvantageous
Complex state Disadvantageous Advantageous
State change logic Disadvantageous Advantageous
Separation of states Disadvantageous Advantageous

5. Practical Example: Creating a ToDo List App

Now we will create a simple ToDo list app using useReducer. This app will include features to add and remove tasks.


import React, { useReducer } from 'react';

const initialState = { todos: [] };

function reducer(state, action) {
  switch (action.type) {
    case 'add':
      return { ...state, todos: [...state.todos, action.payload] };
    case 'remove':
      return { ...state, todos: state.todos.filter((_, index) => index !== action.payload) };
    default:
      throw new Error();
  }
}

function TodoApp() {
  const [state, dispatch] = useReducer(reducer, initialState);
  const [input, setInput] = React.useState('');

  const addTodo = () => {
    dispatch({ type: 'add', payload: input });
    setInput('');
  };

  return (
    <div>
      <h2>ToDo List</h2>
      <input 
        type="text" 
        value={input} 
        onChange={(e) => setInput(e.target.value)} 
      />
      <button onClick={addTodo}>Add</button>
      <ul>
        {state.todos.map((todo, index) => (
          <li key={index}>
            {todo} <button onClick={() => dispatch({ type: 'remove', payload: index })}>Remove</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

The above TodoApp component provides functionality for users to add and remove tasks. It manages state using useReducer, defining logic to add or delete tasks based on each action (type).

6. Advanced Usage of useReducer

Beyond simple state management, useReducer supports advanced features. Here are some of them:

6.1 Loading Initial State

This is a way to load initial state asynchronously. To do this, we use the useEffect hook.


import React, { useReducer, useEffect } from 'react';

function App() {
  const [state, dispatch] = useReducer(reducer, initialState);

  useEffect(() => {
    const fetchData = async () => {
      const response = await fetch('/api/todos');
      const data = await response.json();
      data.forEach(todo => dispatch({ type: 'add', payload: todo }));
    };
    fetchData();
  }, []);

  // Remaining app code...
}

6.2 Nested State Management

You can implement structured state management by combining multiple reducers. When used with the context API, global state management can be handled easily.


function mainReducer(state, action) {
  return {
    counter: counterReducer(state.counter, action),
    todos: todoReducer(state.todos, action),
  };
}

// State and dispatch are provided with multiple reducers.
const [state, dispatch] = useReducer(mainReducer, initialState);

7. Conclusion

useReducer is a powerful tool for building complex state management logic in React. Especially when multiple state changes are necessary, useReducer can enhance the readability and maintainability of your code. Based on what we learned in this tutorial, try utilizing useReducer in your own React applications.

If you have additional examples you want to practice or questions, please leave a comment. We support your journey in React development!

React Course: Everything About useEffect

React is one of the most popular JavaScript libraries for building modern web applications. Among them, the useEffect hook plays a very important role in controlling the component’s lifecycle and managing side effects. In this article, we will cover the basic concepts, usage, and various examples of useEffect in detail.

1. What is useEffect?

useEffect is one of the hooks provided by the React library, used to create side effects in functional components. Side effects include data fetching, manual DOM manipulation, setting timers, or interacting with other external systems.

React components are designed to only define state and props, but sometimes there is a need to perform other actions when the component is rendered. To perform these actions, we use the useEffect hook.

2. Basic usage of useEffect

useEffect can be used with the following syntax:

useEffect(() => {
  // Side effect code
}, [dependency array]);

In the above syntax, the first parameter is the function that performs the side effects, and the second parameter is the dependency array. The dependency array includes the states or props on which this hook depends.

2.1. Simple example

import React, { useEffect, useState } from 'react';

function ExampleComponent() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]);

  return (
    

Current count: {count}

); }

In the above example, the document title is updated whenever count changes. Since count is included in the dependency array, the side effect is executed only when the setCount function is called and count is updated.

3. Timing of useEffect execution

useEffect executes after the component has rendered. In summary:

  • Executed after validation when the component first renders
  • Re-executed when a value specified in the dependency array changes

For example, if the dependency array is empty:

useEffect(() => {
  // Executes only after the first render
}, []);

3.1. Cleanup function

useEffect can return a cleanup function. This cleanup function is called when the component is unmounted or before the next effect runs. This is very useful for preventing memory leaks.

useEffect(() => {
  const timer = setTimeout(() => {
    console.log('Timer executed!');
  }, 1000);

  return () => {
    clearTimeout(timer); // Clear timer when component unmounts
  };
}, []);

4. Advanced usage of useEffect

Having learned the basic usage, let’s move on to advanced concepts of useEffect. We will cover passing detailed parameters for object management, conditional execution, and more.

4.1. Conditional execution

Sometimes, you may want useEffect to execute only when specific conditions are met. This can be implemented using a conditional statement:

useEffect(() => {
  if (count > 0) {
    console.log('Count is positive');
  }
}, [count]);

4.2. Using multiple useEffect

You can use multiple useEffect in a single component to manage each effect independently. Each effect can have its own dependency array.

useEffect(() => {
  // Fetching data
}, [url]);

useEffect(() => {
  // Clear timer
  return () => {
    clearInterval(timerId);
  };
}, []);

5. Case studies of useEffect

Now let’s look at the usefulness of useEffect through real-world use cases.

5.1. API data fetching

Using useEffect is common when fetching API data in a React application:

import React, { useEffect, useState } from 'react';

function DataFetchingComponent() {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch('https://api.example.com/data')
      .then((response) => response.json())
      .then((data) => {
        setData(data);
        setLoading(false);
      });
  }, []);

  if (loading) return 

Loading...

; return (
    {data.map(item =>
  • {item.name}
  • )}
); }

5.2. Real-time data updates

When updating real-time data through WebSockets or Server-Sent Events (SSE), useEffect can be used:

import React, { useEffect, useState } from 'react';

function RealTimeComponent() {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    const socket = new WebSocket('ws://your-websocket-url');

    socket.onmessage = (event) => {
      const newMessage = JSON.parse(event.data);
      setMessages(prevMessages => [...prevMessages, newMessage]);
    };

    return () => {
      socket.close();
    };
  }, []);

  return (
    
{messages.map((msg, index) => (
{msg.content}
))}
); }

6. useEffect vs Lifecycle Methods in Class Components

The useEffect of functional components serves the role of lifecycle methods used in class components (componentDidMount, componentDidUpdate, componentWillUnmount).

For example, the use of useEffect that implements componentDidMount and componentWillUnmount looks like this:

useEffect(() => {
  // Executed when the component is mounted
  fetchData();

  return () => {
    // Executed when the component is unmounted
    cleanup();
  };
}, []);

7. Optimization and performance considerations

When using useEffect, you should consider its impact on performance. Here are some tips to optimize performance:

  • Set the dependency array correctly to prevent unnecessary re-renders.
  • If an effect should only run when certain states change, make that clear.
  • Use cleanup functions appropriately to release persistent memory resources.

8. Frequently Asked Questions (FAQ)

8.1. When is useEffect called?

useEffect is called when the component is mounted or when dependencies are updated.

8.2. Can useEffect be used multiple times?

Yes, multiple useEffect can be used within a single component. Each useEffect is independent and can perform different roles.

8.3. What happens if the dependency array of useEffect is left empty?

If the dependency array is set to an empty array, it will execute only when the component is first mounted and not afterwards.

8.4. Is it okay to call setState inside useEffect?

Yes, you can call setState to update state, which will cause the component to re-render. However, manage dependencies properly to avoid infinite loops.

9. Conclusion

In this tutorial, we thoroughly explored the useEffect hook in React. useEffect is an essential tool for managing side effects and is commonly used in React applications. It may seem difficult at first, but you will naturally become familiar with it as you continue to use it.

Through this article, I hope you have gained an understanding of the powerful features and usage of useEffect and can apply it to your own projects. Practice various use cases like the examples mentioned above to gain additional experience!