Overcoming Challenges and Expanding Horizons: GSoC Adventure
Hey folks,
This is the third blog in the series where I document my journey through the Google Summer of Code program. In this blog, I will be sharing what I have learned and accomplished during weeks 3 and 4. If you're interested in reading about my progress from the first and second weeks, you can find them here.
The issue with GitHub pages
At this stage, the user interface (UI) was nearly ready for publication. However, a major issue arose that needed to be addressed. Our plan was to host the application on GitHub Pages, but we discovered that React Router's BrowserRouter
is not compatible with GitHub Pages. The problem stems from BrowserRouter's use of the browser's history API to store and manage the URL of the current page. Unfortunately, GitHub Pages does not support this API, rendering BrowserRouter unusable.
After conducting some research, I stumbled upon a discussion on Stack Overflow that provided a solution. The alternative we found was to use HashRouter
instead, which is supported by GitHub Pages because it does not rely on the History APIs. It's worth noting, though, that using HashRouter is not recommended for SEO purposes as it is not as search engine friendly as BrowserRouter.
Then, switched to using createHashRouter
instead of HashRouter since new routers were introduced in React Router version 6.4. These new routers provide support for data APIs, allowing us to enhance performance. During this process, I learned about the loader
function, which proved to be quite helpful in directly providing data to React components. Here's an example:
const fetchData = async () => {
const response = await fetch("https://example.com/data");
console.log(response);
return response;
};
const router = createHashRouter([
{
path: "/",
Component: MainContainer,
children: [
{
index: true,
Component: ReportDataHandler,
loader: async () => fetchData(),
}
],
},
]);
const root = createRoot(document.getElementById("root"));
root.render(
<RouterProvider router={router} />
);
In this code snippet, I first defined the routes and mounted the components accordingly. Then, I defined a function called fetchData
responsible for fetching the necessary data and returning the response. This data is then provided to the component using the loader
function. It's important to note that the component will not be rendered until the loader
function is resolved and the data is available.
importance of async/await
One other important thing I learned is about the async/await
keywords, which play a crucial role in function execution. Although JavaScript is a single-threaded language, meaning that only one line of code can be executed at a time, it provides asynchronous features that allow code to run in the background while other code continues execution. This asynchronous behavior is particularly useful when performing tasks that need to run in the background while simultaneously executing the next set of code. However, controlling this asynchronous behavior is sometimes necessary to avoid errors, and that's where the async/await
keywords come into play.
Let's consider an example using the fetchData()
function mentioned earlier. This function is asynchronous and returns a promise. By using the await keyword, we can wait for the promise to be fulfilled before executing the console.log() function.
The async/await keywords bring several benefits. They make asynchronous code more concise and readable, improving the overall code quality. Additionally, they can help enhance the performance of your application by preventing the main thread from being blocked, ensuring smoother execution and responsiveness.
Context APIs in React-router
I also learned about and implemented the Context API in React, which allowed me to make data centrally available in my application. React's Context API provides a way to share data between components without the need to manually pass props down at every level. This can be particularly useful for sharing common data such as themes, locales, or user authentication information.
Let's explore a simple example to better understand how it works:
const ThemeContext = createContext({
theme: "light",
});
const ThemeProvider = ({ children }) => {
return (
<ThemeContext.Provider value={theme}>
{children}
</ThemeContext.Provider>
);
};
const App = () => {
return (
<ThemeProvider>
<MyComponent />
</ThemeProvider>
);
};
const MyComponent = () => {
const theme = useContext(ThemeContext);
return (
<div>
The current theme is {theme}.
</div>
);
};
In this example, we define the ThemeContext
context object to share the theme
variable between the App
component and the MyComponent
component. The ThemeProvider
component wraps the App
component and sets the theme
variable to "light". Within the MyComponent
component, we use the useContext()
hook to access the theme
variable and render it to the screen.
By utilizing the Context API, we can easily provide and consume data across multiple components in a more efficient and organized manner.
Adding new feature
After that, I started working on adding another important feature. When testing the implementation with the Bowtie Tool, it produces result data that needed to be visualized in an intuitive way within our frontend application. Therefore, I focused on adding a new feature that allows users to upload their locally generated data for visualization of their implementation's performance.
Additionally, I incorporated a loading animation to enhance the user experience of the front-end application. After receiving feedback and undergoing reviews from both my mentor and community members, the changes I made were successfully merged into the main branch. (PR links - #241, #297)
Summary: The learning experiences during this period have been enriching, and I'm grateful for the progress made so far. I look forward to sharing more details about my progress in the near future, so stay connected. Thank you for reading this blog until the very end. Peace!