parent
5bbcd85e6c
commit
2e18d5a563
19 changed files with 697 additions and 144 deletions
@ -0,0 +1,65 @@ |
|||||||
|
import { useEffect, useState } from 'react'; |
||||||
|
import ReactConfetti from 'react-confetti'; |
||||||
|
|
||||||
|
type ConfettiPosition = { |
||||||
|
x: number; |
||||||
|
y: number; |
||||||
|
w: number; |
||||||
|
h: number; |
||||||
|
}; |
||||||
|
|
||||||
|
type ConfettiProps = { |
||||||
|
element: HTMLElement | null; |
||||||
|
onDone: () => void; |
||||||
|
}; |
||||||
|
|
||||||
|
export function Confetti(props: ConfettiProps) { |
||||||
|
const { element, onDone } = props; |
||||||
|
|
||||||
|
const [confettiPos, setConfettiPos] = useState< |
||||||
|
undefined | ConfettiPosition |
||||||
|
>(); |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
if (!element) { |
||||||
|
setConfettiPos(undefined); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
const elRect = element.getBoundingClientRect(); |
||||||
|
|
||||||
|
// set confetti position, keeping in mind the scroll values
|
||||||
|
setConfettiPos({ |
||||||
|
x: elRect?.x || 0, |
||||||
|
y: (elRect?.y || 0) + window.scrollY, |
||||||
|
w: elRect?.width || 0, |
||||||
|
h: elRect?.height || 0, |
||||||
|
}); |
||||||
|
}, [element]); |
||||||
|
|
||||||
|
if (!confettiPos) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<ReactConfetti |
||||||
|
height={document.body.scrollHeight} |
||||||
|
numberOfPieces={40} |
||||||
|
recycle={false} |
||||||
|
onConfettiComplete={(confettiInstance) => { |
||||||
|
confettiInstance?.reset(); |
||||||
|
setConfettiPos(undefined); |
||||||
|
onDone(); |
||||||
|
}} |
||||||
|
initialVelocityX={4} |
||||||
|
initialVelocityY={8} |
||||||
|
tweenDuration={25} |
||||||
|
confettiSource={{ |
||||||
|
x: confettiPos.x, |
||||||
|
y: confettiPos.y, |
||||||
|
w: confettiPos.w, |
||||||
|
h: confettiPos.h, |
||||||
|
}} |
||||||
|
/> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,142 @@ |
|||||||
|
/** |
||||||
|
* atom-dark theme for `prism.js` |
||||||
|
* Based on Atom's `atom-dark` theme: https://github.com/atom/atom-dark-syntax |
||||||
|
* @author Joe Gibson (@gibsjose) |
||||||
|
*/ |
||||||
|
code[class*="language-"], |
||||||
|
pre[class*="language-"] { |
||||||
|
color: #c5c8c6; |
||||||
|
text-shadow: 0 1px rgba(0, 0, 0, 0.3); |
||||||
|
font-family: Inconsolata, Monaco, Consolas, 'Courier New', Courier, monospace; |
||||||
|
direction: ltr; |
||||||
|
text-align: left; |
||||||
|
white-space: pre; |
||||||
|
word-spacing: normal; |
||||||
|
word-break: normal; |
||||||
|
line-height: 1.5; |
||||||
|
|
||||||
|
-moz-tab-size: 4; |
||||||
|
-o-tab-size: 4; |
||||||
|
tab-size: 4; |
||||||
|
|
||||||
|
-webkit-hyphens: none; |
||||||
|
-moz-hyphens: none; |
||||||
|
-ms-hyphens: none; |
||||||
|
hyphens: none; |
||||||
|
} |
||||||
|
|
||||||
|
/* Code blocks */ |
||||||
|
pre[class*="language-"] { |
||||||
|
padding: 1em; |
||||||
|
margin: .5em 0; |
||||||
|
overflow: auto; |
||||||
|
border-radius: 0.3em; |
||||||
|
} |
||||||
|
|
||||||
|
:not(pre) > code[class*="language-"], |
||||||
|
pre[class*="language-"] { |
||||||
|
background: #1d1f21; |
||||||
|
} |
||||||
|
|
||||||
|
/* Inline code */ |
||||||
|
:not(pre) > code[class*="language-"] { |
||||||
|
padding: .1em; |
||||||
|
border-radius: .3em; |
||||||
|
} |
||||||
|
|
||||||
|
.token.comment, |
||||||
|
.token.prolog, |
||||||
|
.token.doctype, |
||||||
|
.token.cdata { |
||||||
|
color: #7C7C7C; |
||||||
|
} |
||||||
|
|
||||||
|
.token.punctuation { |
||||||
|
color: #c5c8c6; |
||||||
|
} |
||||||
|
|
||||||
|
.namespace { |
||||||
|
opacity: .7; |
||||||
|
} |
||||||
|
|
||||||
|
.token.property, |
||||||
|
.token.keyword, |
||||||
|
.token.tag { |
||||||
|
color: #96CBFE; |
||||||
|
} |
||||||
|
|
||||||
|
.token.class-name { |
||||||
|
color: #FFFFB6; |
||||||
|
text-decoration: underline; |
||||||
|
} |
||||||
|
|
||||||
|
.token.boolean, |
||||||
|
.token.constant { |
||||||
|
color: #99CC99; |
||||||
|
} |
||||||
|
|
||||||
|
.token.symbol, |
||||||
|
.token.deleted { |
||||||
|
color: #f92672; |
||||||
|
} |
||||||
|
|
||||||
|
.token.number { |
||||||
|
color: #FF73FD; |
||||||
|
} |
||||||
|
|
||||||
|
.token.selector, |
||||||
|
.token.attr-name, |
||||||
|
.token.string, |
||||||
|
.token.char, |
||||||
|
.token.builtin, |
||||||
|
.token.inserted { |
||||||
|
color: #A8FF60; |
||||||
|
} |
||||||
|
|
||||||
|
.token.variable { |
||||||
|
color: #C6C5FE; |
||||||
|
} |
||||||
|
|
||||||
|
.token.operator { |
||||||
|
color: #EDEDED; |
||||||
|
} |
||||||
|
|
||||||
|
.token.entity { |
||||||
|
color: #FFFFB6; |
||||||
|
cursor: help; |
||||||
|
} |
||||||
|
|
||||||
|
.token.url { |
||||||
|
color: #96CBFE; |
||||||
|
} |
||||||
|
|
||||||
|
.language-css .token.string, |
||||||
|
.style .token.string { |
||||||
|
color: #87C38A; |
||||||
|
} |
||||||
|
|
||||||
|
.token.atrule, |
||||||
|
.token.attr-value { |
||||||
|
color: #F9EE98; |
||||||
|
} |
||||||
|
|
||||||
|
.token.function { |
||||||
|
color: #DAD085; |
||||||
|
} |
||||||
|
|
||||||
|
.token.regex { |
||||||
|
color: #E9C062; |
||||||
|
} |
||||||
|
|
||||||
|
.token.important { |
||||||
|
color: #fd971f; |
||||||
|
} |
||||||
|
|
||||||
|
.token.important, |
||||||
|
.token.bold { |
||||||
|
font-weight: bold; |
||||||
|
} |
||||||
|
|
||||||
|
.token.italic { |
||||||
|
font-style: italic; |
||||||
|
} |
@ -1,12 +0,0 @@ |
|||||||
[ |
|
||||||
{ |
|
||||||
"id": "what-is-server", |
|
||||||
"question": "What is a Server", |
|
||||||
"answer": { |
|
||||||
"heading": "", |
|
||||||
"answer": "", |
|
||||||
"list": [], |
|
||||||
"file": "what-is-server.md" |
|
||||||
} |
|
||||||
} |
|
||||||
] |
|
@ -1 +0,0 @@ |
|||||||
## What is Server |
|
@ -0,0 +1,3 @@ |
|||||||
|
JSX stands for JavaScript XML and it is an extension to the JavaScript language syntax. It is used with React to describe what the user interface should look like. By using JSX, we can write HTML structures in the same file that contains JavaScript code. This makes the code easier to understand and debug. |
||||||
|
|
||||||
|
It is basically a syntactic sugar around `React.createElement()` function. |
@ -0,0 +1,28 @@ |
|||||||
|
To improve the performance of React app, we can use the following techniques which are not limited to. I would recommend to read [React docs](https://reactjs.org/docs/optimizing-performance.html) for more details. |
||||||
|
|
||||||
|
- Use production build |
||||||
|
- Use suspense with lazy loading |
||||||
|
- Avoid unnecessary re-renders |
||||||
|
- Memoize expensive computations |
||||||
|
- Monitor performance with React Profiler |
||||||
|
- ... |
||||||
|
|
||||||
|
And more. Here's a test code snippet to demonstrate the performance of React app: |
||||||
|
|
||||||
|
```html |
||||||
|
<!DOCTYPE html> |
||||||
|
<html lang="en"> |
||||||
|
<head> |
||||||
|
<meta charset="UTF-8" /> |
||||||
|
<meta |
||||||
|
name="viewport" |
||||||
|
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" |
||||||
|
/> |
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge" /> |
||||||
|
<title>Document</title> |
||||||
|
</head> |
||||||
|
<body></body> |
||||||
|
</html> |
||||||
|
``` |
||||||
|
|
||||||
|
In the above code snippet, we have a simple HTML page with no CSS and JS. Let's add a React app to it. |
@ -0,0 +1,65 @@ |
|||||||
|
--- |
||||||
|
order: 1 |
||||||
|
briefTitle: 'React' |
||||||
|
briefDescription: 'Test, rate and improve your React knowledge with these questions.' |
||||||
|
title: 'React Questions' |
||||||
|
description: 'Test, rate and improve your React knowledge with these questions.' |
||||||
|
isNew: true |
||||||
|
seo: |
||||||
|
title: 'React Questions' |
||||||
|
description: 'Curated list of React questions to test, rate and improve your knowledge. Questions are based on real world experience and knowledge.' |
||||||
|
keywords: |
||||||
|
- 'react quiz' |
||||||
|
- 'react questions' |
||||||
|
- 'react interview questions' |
||||||
|
- 'react interview' |
||||||
|
- 'react test' |
||||||
|
sitemap: |
||||||
|
priority: 1 |
||||||
|
changefreq: 'monthly' |
||||||
|
questions: |
||||||
|
- question: What is a React? |
||||||
|
answer: React, is an open-source JavaScript library for building user interfaces (UIs). It was developed and is maintained by Meta, and is widely used by developers to create interactive and dynamic web applications. |
||||||
|
topics: |
||||||
|
- 'Basics' |
||||||
|
- 'Beginner' |
||||||
|
- question: What are the features of React? |
||||||
|
answer: Use of Virtual DOM instead of Real DOM, JSX, Server-side rendering, Unidirectional data flow or data binding, Reusable components, etc. |
||||||
|
topics: |
||||||
|
- 'Basics' |
||||||
|
- 'Beginner' |
||||||
|
- question: What is JSX? |
||||||
|
answer: JSX is a syntax extension to JavaScript and comes with the full power of JavaScript. JSX produces React “elements”. You can embed any JavaScript expression in JSX by wrapping it in curly braces. After compilation, JSX expressions become regular JavaScript objects. |
||||||
|
topics: |
||||||
|
- 'Basics' |
||||||
|
- 'Beginner' |
||||||
|
- question: What is the difference between Real DOM and Virtual DOM? |
||||||
|
answer: | |
||||||
|
Virtual DOM is the representation of a UI in the form of a plain javascript object. It is a node tree that lists the elements, their attributes and content as Objects and their properties. Real DOM is the real representation of a UI which can be seen and inspected in the browser. |
||||||
|
Manipulating the virtual DOM is much faster than real DOM, because nothing gets drawn on the screen. React uses this virtual DOM to figure out the most efficient way to update the browser DOM. |
||||||
|
topics: |
||||||
|
- 'Basics' |
||||||
|
- 'Beginner' |
||||||
|
- question: What is the difference between state and props? |
||||||
|
answer: | |
||||||
|
Props are used to pass data from parent to child. They are like function arguments. They are immutable. |
||||||
|
State is managed within the component and is mutable. |
||||||
|
topics: |
||||||
|
- 'Basics' |
||||||
|
- 'Beginner' |
||||||
|
- question: What is the difference between controlled and uncontrolled components? |
||||||
|
answer: controlled-vs-uncontrolled.md |
||||||
|
topics: |
||||||
|
- 'Basics' |
||||||
|
- 'Beginner' |
||||||
|
- question: What are different options to style a React component? |
||||||
|
answer: CSS Stylesheets, Inline styles, CSS Modules, Styled Components, CSS in JS libraries, etc. |
||||||
|
topics: |
||||||
|
- 'Styling' |
||||||
|
- 'Beginner' |
||||||
|
- question: What are different ways to keep React performant? |
||||||
|
answer: 'react-performance.md' |
||||||
|
topics: |
||||||
|
- 'Performance' |
||||||
|
- 'Intermediate' |
||||||
|
--- |
@ -1,8 +1,12 @@ |
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import MarkdownIt from 'markdown-it'; |
import MarkdownIt from 'markdown-it'; |
||||||
|
|
||||||
export function markdownToHtml(markdown: string): string { |
export function markdownToHtml(markdown: string, isInline = true): string { |
||||||
const md = new MarkdownIt(); |
const md = new MarkdownIt(); |
||||||
|
|
||||||
return md.renderInline(markdown); |
if (isInline) { |
||||||
|
return md.renderInline(markdown); |
||||||
|
} else { |
||||||
|
return md.render(markdown); |
||||||
|
} |
||||||
} |
} |
||||||
|
@ -0,0 +1,118 @@ |
|||||||
|
import type { MarkdownFileType } from './file'; |
||||||
|
import slugify from 'slugify'; |
||||||
|
|
||||||
|
interface RawQuestionGroupFrontmatter { |
||||||
|
order: number; |
||||||
|
briefTitle: string; |
||||||
|
briefDescription: string; |
||||||
|
title: string; |
||||||
|
description: string; |
||||||
|
isNew: boolean; |
||||||
|
seo: { |
||||||
|
title: string; |
||||||
|
description: string; |
||||||
|
ogImageUrl?: string; |
||||||
|
keywords: string[]; |
||||||
|
}; |
||||||
|
sitemap: { |
||||||
|
priority: number; |
||||||
|
changefreq: string; |
||||||
|
}; |
||||||
|
questions: { |
||||||
|
question: string; |
||||||
|
answer: string; |
||||||
|
topics: string[]; |
||||||
|
}[]; |
||||||
|
} |
||||||
|
|
||||||
|
type RawQuestionGroupFileType = |
||||||
|
MarkdownFileType<RawQuestionGroupFrontmatter> & { |
||||||
|
id: string; |
||||||
|
}; |
||||||
|
|
||||||
|
export type QuestionType = { |
||||||
|
id: string; |
||||||
|
question: string; |
||||||
|
answer: string; |
||||||
|
isLongAnswer: boolean; |
||||||
|
topics?: string[]; |
||||||
|
}; |
||||||
|
|
||||||
|
export type QuestionGroupType = RawQuestionGroupFileType & { |
||||||
|
questions: QuestionType[]; |
||||||
|
allTopics: string[]; |
||||||
|
}; |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets all the best practice files |
||||||
|
* |
||||||
|
* @returns Promisified BestPracticeFileType[] |
||||||
|
*/ |
||||||
|
export async function getAllQuestionGroups(): Promise<QuestionGroupType[]> { |
||||||
|
const questionGroupFilesMap = |
||||||
|
await import.meta.glob<RawQuestionGroupFileType>( |
||||||
|
`/src/data/question-groups/*/*.md`, |
||||||
|
{ |
||||||
|
eager: true, |
||||||
|
} |
||||||
|
); |
||||||
|
|
||||||
|
const answerFilesMap = await import.meta.glob<string>( |
||||||
|
// get the files inside /src/data/question-groups/[ignore]/content/*.md
|
||||||
|
`/src/data/question-groups/*/content/*.md`, |
||||||
|
{ |
||||||
|
eager: true, |
||||||
|
as: 'raw', |
||||||
|
} |
||||||
|
); |
||||||
|
|
||||||
|
return Object.values(questionGroupFilesMap) |
||||||
|
.map((questionGroupFile) => { |
||||||
|
const fileParts = questionGroupFile?.file?.split('/'); |
||||||
|
const [questionGroupDir, questionGroupFileName] = fileParts?.slice(-2); |
||||||
|
|
||||||
|
const questionGroupFileId = questionGroupFileName?.replace('.md', ''); |
||||||
|
const formattedAnswers: QuestionType[] = |
||||||
|
questionGroupFile.frontmatter.questions.map((qa) => { |
||||||
|
const questionText = qa.question; |
||||||
|
let answerText = qa.answer; |
||||||
|
let isLongAnswer = false; |
||||||
|
|
||||||
|
if (answerText.endsWith('.md')) { |
||||||
|
const answerFilePath = `/src/data/question-groups/${questionGroupDir}/content/${answerText}`; |
||||||
|
answerText = |
||||||
|
answerFilesMap[answerFilePath] || |
||||||
|
`File missing: ${answerFilePath}`; |
||||||
|
|
||||||
|
isLongAnswer = true; |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
id: slugify(questionText, { lower: true }), |
||||||
|
question: questionText, |
||||||
|
answer: answerText, |
||||||
|
topics: qa.topics, |
||||||
|
isLongAnswer, |
||||||
|
}; |
||||||
|
}); |
||||||
|
|
||||||
|
const uniqueTopics = formattedAnswers |
||||||
|
.flatMap((answer) => answer.topics || []) |
||||||
|
.filter((topic) => topic) |
||||||
|
.reduce((acc, topic) => { |
||||||
|
if (!acc.includes(topic)) { |
||||||
|
acc.push(topic); |
||||||
|
} |
||||||
|
|
||||||
|
return acc; |
||||||
|
}, [] as string[]); |
||||||
|
|
||||||
|
return { |
||||||
|
...questionGroupFile, |
||||||
|
id: questionGroupFileId, |
||||||
|
questions: formattedAnswers, |
||||||
|
allTopics: uniqueTopics, |
||||||
|
}; |
||||||
|
}) |
||||||
|
.sort((a, b) => a.frontmatter.order - b.frontmatter.order); |
||||||
|
} |
Loading…
Reference in new issue