60. Module Introduction
In this course section, we will delve deeper into key React concepts covered previously. We will examine JSX and non-JSX code, revisit Components and State for better structure, and explore advanced state usage, patterns, and best practices. We’ll start by enhancing the demo application from the last section, and later, we’ll build a tic-tac-toe game to explore advanced concepts.
61. You Don’t Have To Use JSX!
This section discusses JSX code in React, emphasizing that while JSX is a non-standard feature not directly supported by browsers, it simplifies the development process. JSX code is transformed by a build process into browser-compatible code. Although it’s possible to create React applications without JSX using the createElement
method from React, this approach is more verbose and less intuitive. Most React projects, including those in this course, will utilize JSX for its convenience and readability. The text also highlights that using the non-JSX method requires no build process but is typically more complex. A practical example is provided, demonstrating how to create a root component using both JSX and the non-JSX approach while achieving the same result on screen.
62. Working with Fragments
The discussion focuses on JSX and its requirement for a single parent element in a return statement. In an example using a div
to wrap sibling elements, it is explained that removing the div
leads to an error because JSX must return one parent element. This limitation is likened to JavaScript’s inability to return multiple values from a function. To avoid unnecessary DOM elements, React provides a Fragment
component, which can be used as a wrapper without rendering an actual element. Additionally, modern React projects allow for an even shorter syntax using empty tags to achieve the same effect without extra code or DOM elements.
- Working with Fragments
63. When Should You Split Components
The discussion focuses on the challenges of managing a large React component, specifically the App
component, which has multiple responsibilities such as rendering core concepts, managing tap buttons, and handling interactive states. The current structure leads to inefficiencies, as state updates cause unnecessary re-executions of the header component, resulting in unintended behavior like generating a new random number each time. This indicates the need to split the app component into smaller, more focused sub-components to better manage responsibilities and improve performance, a crucial skill for React developers.
64. Splitting Components By Feature & State
The text discusses breaking down a large React App Component into smaller, more manageable components, which is a common best practice in React development. The process involves creating two new components: CoreConcepts
and Examples
.
-
Core Concepts Component:
-
The relevant section of the App Component is cut out and pasted into a new
CoreConcepts.jsx
file. -
The component imports necessary data and exports a function that returns the JSX for Core Concepts.
-
-
Examples Component:
-
Similarly, the section for examples, including tab buttons and content, is transferred to a new
Examples.jsx
file. -
This component also handles its own state and imports necessary data and React hooks.
-
-
App Component Adjustments:
-
The App Component is cleaned up by removing imports related to the now-extracted features and importing the new components instead.
-
The state management for the tab content is moved to the Examples Component, which affects how the application behaves when tabs are clicked.
-
The overall goal is to create a leaner App Component by distributing features across smaller components, making the codebase easier to manage, especially in larger projects. After these changes, the application should function identically to before, without errors, while now having a more organized structure.
- Splitting Components
65. Problem - Props Are Not Forwarded To Inner Elements
In this section, the focus is on enhancing an existing React application by creating a reusable Section
component. The idea is to maintain a consistent structure for sections in the application, which typically includes a title and content. The Section
component will accept props
, specifically a title
and children
, allowing for dynamic content rendering.
The process involves:
-
Creating a
section.jsx
file in the components folder. -
Defining the
Section
component to return a section element with anh2
title and the content passed throughchildren
. -
Importing and using the
Section
component in theexamples.jsx
file, replacing existing section markup with the new component.
However, an issue arises with styling because when props are set on a custom component, they are not automatically forwarded to the underlying HTML elements. This results in lost styling, as the ID
prop set on the Section
component was not passed to the actual section element.
To resolve this, it’s suggested to destructure and manually pass props like ID
and className
to the built-in section element. However, this approach can become cumbersome with multiple attributes. A more scalable solution involves using a pattern known as "forwarding props," which allows for easier management of attributes without needing to manually destructure each one.
66. Forwarding Props To Wrapped Elements
The passage explains how to use JavaScript’s destructuring and spread syntax when creating custom components in React. By using the spread operator (three dots), developers can collect all additional props passed to a component and merge them into a props
object. This allows for forwarding those props to built-in elements, maintaining flexibility in the component’s usage without manually extracting each prop.
The example specifically discusses a Section
component that utilizes this pattern to forward various props like ID and class name to a built-in Section
element. It highlights the benefits of this approach in creating wrapper components, ensuring they remain functional and flexible. The same technique can be applied to other components, such as TabButton
, by spreading any remaining props onto a built-in button and replacing custom props with standard ones (e.g., replacing onSelect
with onClick
). This maintains the original functionality while simplifying prop management in the components.
- Forwarding Props
67. Working with Multiple JSX Slots
The section discusses the creation of a reusable tabs component in a React project, emphasizing the props forwarding pattern. The current setup for tabs consists of a menu bar with buttons and content displayed below, which may work for simple applications but could become cumbersome in larger ones.
To improve reusability, a new Tabs
component is proposed, allowing the use of prop destructuring to manage dynamic tab content. The author suggests managing tab button clicks and content outside the Tabs
component to maintain its status as a "dumb" wrapper.
To accomplish this, an additional prop (e.g., buttons
) is introduced to allow passing tab buttons as JSX, alongside a children
prop for content. This enables the Tabs
component to have a clear structure: buttons inside a menu element and content below it. The example demonstrates how to implement this pattern, which, although seemingly redundant for simple applications, is essential for scalable React development.
- Multiple JSX Slots
68. Setting Component Types Dynamically
The discussion focuses on enhancing a Tabs component by allowing it to accept a buttonsContainer
prop that determines the wrapper element for buttons within the component. This approach aims to improve flexibility, enabling developers to choose different wrapper elements (like menu
, ul
, div
, or custom components) when using the Tabs component in various parts of an application.
To implement this, the idea is to create a variable that starts with a capital letter (e.g., ButtonsContainer
) to reference the buttonsContainer
prop within the component. This allows React to treat the prop’s value correctly, whether it’s a built-in HTML element or a custom component.
Key points to remember include:
-
Built-in elements should be passed as string identifiers (e.g., "menu").
-
Custom components should be passed as identifiers without angle brackets, and they must start with an uppercase character to be recognized as components.
This pattern enhances the reusability of the Tabs component while maintaining a clean separation between buttons and content.
- Setting Component Types Dynamically
69. Setting Default Prop Values
The discussion focuses on the concept of default prop values in React, specifically using the Tabs component. The ButtonsContainer prop, which designates a wrapper for buttons, is highlighted as an example where a default value can enhance usability. By utilizing destructuring syntax in the component definition, a default value (such as "menu") can be assigned to the ButtonsContainer
prop. This allows the Tabs component to function without explicitly setting the ButtonsContainer, thereby simplifying its usage while maintaining the same functionality. The example demonstrates that even without specifying the prop, the Tabs component defaults to using the menu element as the wrapper.
Coding Exercise 15: Creating Flexible Components
export default function Button({ children, mode="filled", Icon, ...props }) {
const noIconClass = `button ${mode}-button`;
const withIconClass = noIconClass + " icon-button";
return (
<button className={Icon ? withIconClass : noIconClass} {...props}>
{
Icon ?
<span className="button-icon"><Icon /></span> :
''
}
<span>{children}</span>
</button>
)
}
70. Onwards To The Next Project & Advanced Concepts
The section discusses the initial setup for a tic-tac-toe game using React. It begins by explaining the intention to create a header with an image and title. Instead of adding this directly to the app component in App.jsx
, the author decides to place a simple "coming soon" paragraph there and moves the header markup to the index.html
file, which is served to visitors. This approach is justified because the header is static and does not rely on React’s props or state. The author highlights that static content can be directly added to index.html
, while dynamic content should be managed within React components. Additionally, the project includes a public folder for images, and the author demonstrates how to reference an image from this folder in index.html
. The alt text for the image is specified, and the section concludes by indicating readiness to proceed with developing the game logic in the React components.
71. Not All Content Must Go Into Components
The discussion focuses on building a tic-tac-toe game and highlights the importance of understanding various patterns and concepts during development. The initial step involves adding a header to the application that consists of an image and a title. Instead of placing this header directly into the main React component (App.jsx
), the author suggests adding static markup directly into the index.html file, which serves the initial HTML to website visitors. This is emphasized as a valid approach for static content that doesn’t depend on React’s state or props.
The author explains how to reference images stored in the public folder without needing to define a path, as these files are served alongside the index.html. The example given refers to an image named "game-logo.png" with appropriate alt text. After implementing these changes, the header should display correctly when the application is reloaded. Finally, the author indicates a transition to working on the React components to develop the game logic.
- Static Content
72. Closer Look - public vs assets for Image Storage
The document explains the use of two folders in a web development project: public/
and src/assets/
.
-
public/ Folder: Files stored here, such as images, are publicly accessible and can be directly referenced in
index.html
orindex.css
. They can be accessed via a browser, for example, throughlocalhost:5173/some-image.jpg
. -
src/assets/ Folder: Files in this folder are not publicly accessible and cannot be directly loaded by website visitors. Instead, they are used in code files, where they are processed and optimized by the build system before being made available in the
public/
folder.
Usage Guidelines:
-
Use the
public/
folder for files that should be publicly available and not processed by the build system (e.g., favicons). -
Use the
src/
folder for images needed within components, as these will be handled by the build process.
73. New Project - First Steps Towards Our Tic-Tac-Toe Game
The task involves developing a main game component for a tic-tac-toe web application. The component will include three primary building blocks: a player name display and editing area, a game board, and a log for tracking player turns.
To start, the developer will create a main wrapper element and a "game container" div for organizing the layout. Inside the container, an ordered list will be used to display the players' names and symbols (X for player one and O for player two). Each player’s name will be wrapped in a span with the class "player name," while their symbol will be in another span with the class "player symbol."
Currently, the player names are hard-coded, but the developer plans to make them dynamic and add functionality for editing the names in the future. Once the basic structure is set up, the next step will be to implement the editing feature.
74. Concept Repetition - Splitting Components & Building Reusable Components
The passage discusses the process of enhancing a React application by adding an "Edit" button next to player names and symbols. It highlights the need to avoid repeating markup for players in the App component, suggesting the creation of a separate Player component to encapsulate the repeated structure. This involves creating a components folder and a Player.jsx file, where the Player component accepts props for the player’s name and symbol. The existing repeated markup is moved to this new component, streamlining the code by allowing for the use of props to render player-specific data. The final step involves importing and utilizing the Player component in the App component, thereby improving code organization while setting the stage for future functionality of the Edit button.
75. Concept Repetition - Working with State
The task involves implementing an edit feature for a player’s name in a React component. When the "Edit" button is clicked, it should display an input field for the user to update the player’s name, replacing the displayed name. The button should also change its label to "Save" while in edit mode.
To achieve this, you’ll need to manage a state using useState
to track whether the component is in editing mode (a Boolean value). Initially, the state is set to false
. A function named handleEditClick
is created to set the editing state to true
when the button is clicked.
Next, conditional rendering is implemented: if the editing state is false
, the player’s name is displayed; if true
, an input field appears. The input field should be set to accept text and will be enhanced further later to allow saving the changes and pre-populating it with the current player’s name. Overall, this implementation allows for basic interactivity in the player component.
- Working with State
76. Component Instances Work In Isolation!
The speaker emphasizes an important feature of React: component isolation. When reusing components, such as the player component in the application, each instance functions independently. Changes in one instance do not affect others, allowing for complex, reusable components that do not interfere with each other. This isolation is crucial for maintaining the integrity of individual components, ensuring that actions like editing only impact the intended instance.
77. Conditional Content & A Suboptimal Way Of Updating State
The task involves modifying a user interface to toggle between an editable input field and displaying the player’s name. The edit button’s label should change between "Edit" and "Save" based on whether the input field is active or not.
To implement this:
-
Button Caption: Introduce a dynamic value for the button caption using a variable (
btnCaption
) or a ternary expression based on theisEditing
state. Default to "Edit" and change to "Save" when editing. -
Pre-Populate Input: Set the value of the input field to the current player’s name using the
name
prop to ensure it reflects the correct player’s name. -
Toggle Edit Mode: Modify the
handleEditClick
function to toggle theisEditing
state. Instead of using a ternary expression to switch the state, a simpler approach is to use the negation operator (!
), which inverts the current state.
By implementing these changes, the interface will allow users to edit player names and toggle between edit and display modes effectively.
78. Best Practice - Updating State Based On Old State Correctly
In React, when updating state based on its previous value, it is recommended to use a functional approach by passing a function to the state updater (e.g., setIsEditing
). This method ensures that React provides the most current state value when executing the update, preventing potential issues with asynchronous state updates.
Using the functional approach guarantees that each state update reflects the latest state, as opposed to using the current state directly, which may lead to unexpected behavior when multiple updates are scheduled in quick succession. It is essential for React developers to adopt this best practice to ensure reliable and accurate state management in their components.
- Updating State Based On Old State
79. User Input & Two-Way-Binding
The passage discusses how to enable editing of a player name in a React component. Initially, the input field does not allow editing because the value prop is set, which prevents user input from being reflected. The author suggests using a default value prop instead, but this doesn’t save changes. Instead, a better approach is to use the useState
hook to manage the player name as a piece of state, allowing the component to update and reflect changes.
The following steps are outlined:
-
Introduce a new state variable for the player name.
-
Rename the variable to avoid conflicts and set its initial value from a prop.
-
Create a
handleChange
function to update the state when the user types in the input field. -
Attach the
handleChange
function to the input’sonChange
event to capture user input.
By doing this, both the input field and the displayed player name will update correctly, allowing for two-way binding where the input reflects changes made by the user. The author concludes by explaining that this method allows the component to manage user input effectively, ensuring updated values are saved and displayed correctly.
- Two-Way-Binding
80. Rendering Multi-Dimensional Lists
The player functionality for a tic-tac-toe game has been completed, allowing for name editing, and the next step is to create the game board. This involves developing a GameBoard
component that displays a three-by-three grid using a list structure populated with buttons. Instead of hardcoding the grid, an initialGameBoard
constant is defined as an array of arrays, initially filled with null
values. This setup allows for dynamic updates when players click on squares.
The grid is rendered using the map
method to iterate over the rows and columns, creating list items for each square. Each button will either display an 'X', an 'O', or nothing based on player interactions, though the logic for updating the state on clicks is not yet implemented. The GameBoard
component is then integrated into the main app component, resulting in a visible grid with clickable buttons, setting the stage for the next phase of adding game logic.
- Multi-Dimensional Lists
81. Best Practice - Updating Object State Immutably
The text describes the process of implementing state management in a React component for a game board, specifically for a Tic-Tac-Toe game.
-
Component Setup: The
GameBoard
component is set up to dynamically render a grid with buttons representing game squares. -
State Management: The
useState
hook is utilized to manage the game board state, which is initialized with a multidimensional array calledinitialGameBoard
. -
Handling Button Clicks: A function named
handleSelectSquare
is created to update the game board when a button is clicked. This function uses thesetGameBoard
to replace the corresponding square’s value (fromnull
to either 'X' or 'O') based on the player’s turn. -
Immutable State Update: It is emphasized that state updates for objects or arrays should be done immutably to avoid bugs. This involves creating a new array and copying the existing elements before making any updates.
-
Event Handling with Parameters: To pass the necessary row and column indices to
handleSelectSquare
, an anonymous function is created for theonClick
event of the buttons. -
Final Implementation: After implementing the above logic, clicking the buttons updates the game board correctly. However, the current implementation does not handle turn switching or win conditions.
Overall, the focus is on correctly managing and updating the game board state in a React component, while adhering to best practices for state management in JavaScript.
- Updating Object State Immutably
82. Lifting State Up (Core Concept)
The text discusses the implementation of a two-player game board in React, focusing on player turn management and UI feedback. It outlines the need to switch between players, highlight the active player using CSS classes, and manage the active player state in a common ancestor component (the App component).
Key steps include:
-
State Management: The active player state is lifted to the App component to allow both Player and GameBoard components to access it.
-
Functionality: A function (
handleSelectSquare
) is created to handle turn switching when a square is clicked, updating the active player. -
Props and Component Interaction: The GameBoard component receives a prop to execute the turn-switching function, while the Player component receives an
isActive
prop to conditionally apply a CSS class for highlighting. -
Dynamic UI Updates: The UI dynamically reflects the current active player and their symbols on the game board, providing visual feedback during gameplay.
The concept of "lifting state up" is emphasized as a crucial technique in React for managing shared state across components. The implementation allows for player interaction but notes that additional features like win conditions and preventing multiple clicks on the same button still need to be addressed.
- Lifting State Up
83. Avoid Intersecting States
The focus is on improving a React application by implementing a log component that tracks the turns taken in a game. The log will display an ordered list of player turns, necessitating the management of a dynamic array of turns. To achieve this, state management needs to be lifted from the game board component to the app component, which has access to both the game board and the log. This approach will prevent redundancy in storing turn data, as the game board already reflects which buttons were clicked, albeit without the order of clicks. Instead of duplicating state, the application will manage a single array of game turns, allowing both the log and the game board to derive necessary information from it. The existing game board state that lacks ordering will be commented out, emphasizing the need for a more efficient state management strategy.
- Avoid Intersecting States
84. Prefer Computed Values & Avoid Unnecessary State Management
The GameBoard component is being modified to remove the activePlayerSymbol
prop and instead use the onSelectSquare
prop for handling clicks on the squares. The game state is being lifted to the App component. The handleSelectSquare
function will now update the turns array, ensuring that the state is updated immutably. Each turn will be represented as an object containing the player’s symbol and the row and column indices of the clicked square.
The implementation ensures that the current player’s symbol is determined without merging different state values, using a currentPlayer
variable that checks the latest turn to switch between players X and O. This structure allows for a clear representation of the game’s state and enables future development for displaying the game log and updating the GameBoard accordingly.
- Prefer Computed Values
85. Deriving State From Props
The passage outlines the implementation details of a game board component in a React application. The goal is to derive the game board’s state from an array of game turns. Key steps include:
-
Passing Props: A new
turns
prop is passed to theGameboard
component, which contains the array of turns. -
State Derivation: The game board is initialized with its default structure. A loop iterates through the
turns
array to extract relevant information (square and player) from each turn’s object. This information is used to update the corresponding cell in the game board. -
Object Destructuring: The code employs object destructuring to efficiently extract properties from the turn objects and their nested square objects.
-
Error Handling: An error occurs due to missing row and column indices in the
handleSelectSquare
function’s implementation, which is tied to the button’sonClick
event. -
Fixing the Error: The solution involves using an anonymous function in the
onClick
event to pass the row and column indices, ensuring the correct data is provided tohandleSelectSquare
. -
Outcome: After implementing these changes, the game board updates correctly upon button clicks and can reflect the current game state derived from the
turns
array. Additionally, there’s a plan to log the game turns for better debugging and oversight in future lectures.
86. Sharing State Across Components
The discussion focuses on implementing a Log component in a React application to display game turns. The Log component will receive a turns
prop from the App component, which contains the game turn data. Each turn will be mapped to a list item showing which player selected which field, identified by its row and column indices. The player information is represented by a symbol, while the unique key for each list item is generated using a combination of the row and column indices. The template literal syntax in JavaScript will be used to create a string for the key and log message. Finally, the turns
prop must be set in the App component to pass the game turns state, enabling the Log component to update and display the log messages correctly when fields are selected.
- Sharing State
87. Reducing State Management & Identifying Unnecessary State
In the recent lectures, the focus was on improving the management of game state in a React application by eliminating unnecessary state variables. The activePlayer state, which was previously managed separately, can be derived from the existing gameTurns state. This is because the active player changes with each turn, and this information can be obtained directly from gameTurns instead of maintaining an additional state.
To achieve this, a helper function called deriveActivePlayer
is proposed, which calculates the current active player based on the gameTurns state. This function is defined outside of the component to avoid unnecessary re-creation on re-renders. By using this helper function, the code becomes cleaner, reducing duplication and managing less state. The component can then call this function to set the active player both in the main component and when updating game turns.
The discussion concludes with the next steps, which include preventing players from clicking the same button multiple times and implementing game-over checks.
- Reducing State Management
88. Disabling Buttons Conditionally
The discussion focuses on preventing multiple clicks on a button in a game, which is essential for proper gameplay and log management. To achieve this, the button in the GameBoard component can be dynamically disabled based on whether it has already been selected. This is done by using a disabled
prop that checks if the player symbol
is either 'X' or 'O' (indicating the button has been clicked) or null
(indicating it can still be clicked). If the player symbol is not null
, the button is disabled; otherwise, it remains enabled. As a result, once a button is clicked, it cannot be clicked again, ensuring each button is only selectable once and the game’s functionality is maintained.
- Disabling Buttons
89. Outsourcing Data Into A Separate File
The text discusses implementing a feature to check for a player’s victory in a game. It highlights that the check for winning combinations should occur after every turn to determine if the game has ended. The author suggests placing this logic in the app component, where game-over information is needed. They plan to create a constant that holds all possible winning combinations as an array of arrays, with each inner array representing a specific combination that leads to a win. The explanation includes how to define these combinations using row and column indices, following JavaScript’s zero-based indexing. The author also mentions that a file named winning_combinations.js
is provided, which contains the necessary winning combinations, and instructs on how to import this file into the app’s JSX.
- Outsourcing Data Into A Separate File
90. Lifting Computed Values Up
The text explains how to dynamically check for winning combinations in a game after each turn in a React application. The main steps discussed include:
-
State Management: Introduce a state variable (
hasWinner
) to track if there is a winner. While initially suggested, it is deemed redundant since the winner can be derived from thegameTurns
array. -
Winning Combination Check: Instead of checking for a winner in the
handleSelectSquare
function, the component will re-evaluate winning combinations every time it re-renders after a turn. This involves iterating through predefined winning combinations and checking the symbols in the game board. -
Game Board Access: The game board’s data is initially in a separate component. The solution involves moving the logic to derive the game board into the main app component, allowing access to the game board’s symbols for winner evaluation.
-
Component Structure: The game board component will receive the derived game board as a prop instead of computing it internally, simplifying its code and improving structure.
Overall, the approach emphasizes deriving state from existing data and structuring components effectively in React.
91. Deriving Computed Values From Other Computed Values
The content discusses a method to check for a winning combination in a game, likely a tic-tac-toe scenario. It explains how to access a multidimensional game board array to retrieve symbols stored in specific squares that make up a winning combination. The process involves iterating through possible winning combinations, checking if the symbols in the squares are equal, and confirming that they are truthy (not null). If all conditions are met, it identifies a winner and sets a variable to represent the winning symbol. The text also mentions displaying a message when a player wins, such as "You won X" or "You won O." However, it notes that after a win, players can continue playing, which is not the desired behavior, indicating the need for a more refined game over screen.
- Deriving Computed Values
92. Tic-Tac-Toe Game - The Game Over Screen & Checking for a Draw
The task is to add a GameOver component to an app, which will display when the game concludes. This component will show the winner’s name or indicate a draw, and include a button to restart the game. The GameOver component will receive the winner’s name as a prop.
The logic in the app component will be adjusted to check for both a winner and a draw. A draw will be determined if all nine game turns have been played without a winner. The GameOver component will be displayed if there is a winner or if a draw occurs, handling the rendering accordingly based on whether a winner is present.
Lastly, while the basic functionality is set up and the GameOver screen displays correctly for both winning and drawing scenarios, the button to restart the game still needs to be implemented.
- Checking for a Draw
93. Why Immutability Matters - Always!
The task involves implementing a rematch feature in a game by resetting the gameTurns
state, which serves as the central data source for the game’s logic. To achieve this, a handleRestart
function is created in the App component to set gameTurns
to an empty array. This function is then passed as a prop to the GameOver component, where it is linked to a button’s onClick event.
However, a bug was identified related to how the game board is updated. When the board is modified, it directly alters the original array in memory due to JavaScript’s reference value behavior with arrays. This leads to issues when the game is restarted, as the modified game board persists instead of resetting.
The solution involves creating a deep copy of the initialGameBoard
to ensure that changes in the game board do not affect the original array. By using map
to copy inner arrays, a new game board is generated each time. After implementing these changes, the rematch feature works correctly, allowing the game to restart with a cleared log and no errors.
- Why Immutability Matters
94. When NOT To Lift State Up
The discussion revolves around enhancing a game application by displaying the names of players instead of just indicating which symbol won (X or O). Currently, player names are stored in the player component, but to display them in the app component, the names need to be lifted to the app’s state. However, lifting the state could cause unnecessary re-renders of the entire app on every keystroke, which is inefficient.
Instead, the solution is to maintain the player name state in the app component as an object mapping symbols (X and O) to player names (Player One and Player Two). This allows the app to update player names only when a save button is clicked, rather than on every keystroke. A new function, handlePlayerNameChange
, is proposed to handle the name changes by updating the state based on the previous player names, ensuring that only the changed name is updated while preserving the other player’s name. This approach efficiently manages player data without unnecessary re-renders.
- When NOT To Lift State Up
95. An Alternative To Lifting State Up
The process described outlines how to update player data in a game application. It involves triggering a function, handlePlayerNameChange
, whenever the player component is interacted with, specifically when the "Save" button is clicked. The function is passed as a prop, onChangeName
, to the player components. Inside the player component, this prop is destructured and used in the handleEditClick
function to update the player’s name based on the current state and the editing status.
Additionally, the application uses the players' state to display the correct player name when the game concludes. It dynamically accesses the winner’s name based on the winning symbol (either X or O) and updates the display accordingly. After implementing these changes, the game can show the winner’s name and allow for name changes, completing the game’s functionality.
- An Alternative To Lifting State Up
96. Final Polishing & Improving Components
The game development process is complete, but the code for the app component needs improvement for better readability and organization. The plan involves refactoring by outsourcing logic into separate functions. Specifically, a new function called deriveWinner
will handle the determination of the winner, taking the game board and player data as parameters and returning the winner. Similarly, the logic for deriving the game board will be moved to another function called deriveGameBoard
, accepting game turns as input.
Additionally, a new constant named players
will be introduced to store player names, making the code more maintainable by avoiding hard-coded values. The naming conventions for constants will also be standardized for clarity. After these changes, the app component will become leaner and more readable, while functionality remains intact. The developer will gain valuable experience with components and state management, along with important React patterns and concepts.
- Improving Components