- React Ecosystem
- Folder Structure
- Next Open Source Projects
- React Best Practices
- Interview Questions
- Tailwind CSS
-
- Tailwind CSS
- styled-components
- Emotion
- matcha Make naked websites look great
- 98.css
- XP.css
- 7.css
-
Other UI libs
-
- React Developer Tools (chrome link)
- Redux DevTools (chrome link)
- Testing Playground (chrome link)
- React Hook Form DevTools
- TanStack Query DevTools
- Fake Filler (chrome link)
- Responsive Viewer (chrome link)
- Web Developer (chrome link)
- CSS Peeper (chrome link)
- CSS Viewer for Google Chrome (chrome link)
- Project Naptha (chrome link)
-
- mitosis Write components once, run everywhere.
- million
- avvvatars
- Supabase
- appwrite
- payloadcms
- watermelondb
- react-markdown
- react-tweet
- react-advanced-cropper
- craft.js
1. Components:
- Reusable pieces of UI that encapsulate their own logic and rendering.
- Can be classified as:
- Functional Components: Simple JavaScript functions that return JSX.
- Class Components: Extend React.Component and manage state and lifecycle methods using
this
.
2. JSX:
- A syntax extension for JavaScript that allows writing HTML-like structures within code.
- Elements in JSX can represent both React components and DOM elements.
3. Props:
- Data passed from parent components to child components.
- Used to customize the behavior and appearance of child components.
4. State:
- Data that can change over time within a component.
- Managed using the
useState
hook in functional components orthis.state
in class components. - Changes to state trigger re-renders of the component and its children.
5. Rendering:
- The process of generating the UI based on the component's state and props.
- React uses a virtual DOM to efficiently update only the necessary parts of the actual DOM.
6. Lifecycle Methods:
- Methods that are called at different stages of a component's lifecycle (mounting, updating, unmounting).
- Used for tasks like data fetching, subscriptions, or cleanup.
- Available in class components, but functional components can use hooks for similar effects.
7. Hooks:
- Functions that allow you to "hook into" React features like state and lifecycle methods from functional components.
- Examples include
useState
,useEffect
,useContext
,useMemo
, and more.
8. Virtual DOM:
- An in-memory representation of the actual DOM.
- React uses it to compare changes and apply only the necessary updates to the real DOM, improving performance.
9. Reconciliation:
- The process of comparing the virtual DOM with the actual DOM to determine the minimal changes needed.
- React's efficient reconciliation algorithm is a key factor in its performance.
// The useMemo hook is used to memoize the result of a computation. Memoization is a technique where the result of a function is cached and returned when the same inputs occur again, instead of recomputing the result. This can be useful for optimizing performance in certain scenarios, especially when dealing with expensive calculations or rendering.
import React, { useState, useMemo } from "react";
function App() {
const [count, setCount] = useState(0);
const [toDos, setToDos] = useState([{ id: 1, text: "Learn React" }]);
const filteredToDos = useMemo(() => {
// Expensive filtering operation
return toDos.filter((todo) => todo.id !== count);
}, [toDos, count]);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<ul>
{filteredToDos.map((todo) => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
</div>
);
}
└── src/
├── assets/
├── api/
├── configs/
├── components/
│ ├── SignUpForm.tsx
│ ├── Employees.tsx
│ ├── PaymentForm.tsx
│ └── Button.tsx
├── hooks/
│ ├── usePayment.ts
│ ├── useUpdateEmployee.ts
│ ├── useEmployees.ts
│ └── useAuth.tsx
├── lib/
├── services/
├── states/
├── utils/
├── main.tsx
└── App.tsx
└── src/
├── assets/
├── api/
├── configs/
├── components/
│ ├── auth/
│ │ └── SignUpForm.tsx
│ ├── payment/
│ │ └── PaymentForm.tsx
│ ├── common/
│ │ └── Button.tsx
│ └── employees/
│ ├── EmployeeList.tsx
│ └── EmployeeSummary.tsx
├── hooks/
│ ├── auth/
│ │ └── useAuth.ts
│ ├── payment/
│ │ └── usePayment.ts
│ └── employees/
│ ├── useEmployees.ts
│ └── useUpdateEmployee.ts
├── lib/
├── services/
├── states/
├── utils/
├── main.tsx
└── App.tsx
└── src/
├── assets/
├── core/
│ ├── services/
│ ├── store/
│ ├── api/
│ └── ...
├── modules/
│ ├── payment/
│ │ ├── components/
│ │ │ └── PaymentForm.tsx
│ │ ├── hooks/
│ │ │ └── usePayment.ts
│ │ ├── index.tsx
│ │ └── ...
│ ├── auth/
│ │ ├── components/
│ │ │ └── SignUpForm.tsx
│ │ ├── index.tsx
│ │ └── ...
│ ├── employees/
│ │ ├── components/
│ │ │ ├── EmployeeList.tsx
│ │ │ └── EmployeeSummary.tsx
│ │ ├── hooks/
│ │ │ ├── useEmployees.ts
│ │ │ └── useUpdateEmployee.ts
│ │ ├── index.tsx
│ │ └── ...
│ └── ...
├── shared/
│ ├── components/
│ │ └── Button.tsx
│ ├── constants/
│ ├── enums/
│ ├── hooks/
│ │ └── useAuth.ts
│ ├── models/
│ ├── types/
│ ├── utils/
│ └── ...
└── ...
Note
These are just to give you an idea about how to structure you project and not the absolute you should always follow.
export default function App() {
const [searchParam, setSearchParam] = useSearchParam({ q: "", isActive: false });
const q = searchParam.get("q");
const isActive = searchParam.get("isActive") === "true";
return (
<>
<input
type="text"
id="q"
value={q}
onChange={(e) =>
setSearchParam(
(prev) => {
prev.set("q", e.target.value);
return prev;
},
{ replace: true },
)
}
/>
<input
type="checkbox"
id="isActive"
value={isActive}
onChange={(e) =>
setSearchParam((prev) => {
prev.set("isActive", e.target.checked);
return prev;
})
}
/>
</>
);
}
export default function App() {
const [array, setArray] = useState([1, 2, 3]);
const addToStart = (num) => {
setArray((prev) => {
return [num, ...prev];
});
};
const addToEnd = (num) => {
setArray((prev) => {
return [...prev, num];
});
};
return (
<>
{array}
<button
onClick={() => {
addToStart(0);
addToEnd(0);
}}
>
Add
</button>
</>
);
}
// Add this to the page components and change the title based on the page
document.title = "A new title";
// ES5
function getSum(a, b) {
return a + b;
}
// ES6
const getSum = (a, b) => a + b;
// ES5
var name = "Name";
console.log("My name is " + name);
// ES6
const name = "Name";
console.log(`My name is ${name}`);
// ES5
var fruits = ["apple", "banana"];
// ES6
let fruits = ["apple", "banana"];
fruits.push("mango");
const workingHours = 8;
var person = {
name: "John",
age: 32,
};
// ES5
var name = person.name;
var age = person.age;
// ES6
const { name, age } = person;
var name = "John";
var age = 32;
var designations = "Game Developer";
var workingHours = 8;
// ES5
var person = {
name: name,
age: age,
designation: designation,
workingHours: workingHours,
};
// ES6
const person = { name, age, designation, workingHours };
const helloText = () => <div>Hello</div>; // wrong
const HelloText = () => <div>Hello</div>; // correct
const working_hours = 10; // bad approach
const workingHours = 10; // good approach
const get_sum = (a, b) => a + b; // bad approach
const getSum = (a, b) => a + b; // good approach
<!--bad approach-->
<div className="hello_word" id="hello_world">Hello World</div>
<!--good approach -->
<div className="hello-word" id="hello-world">Hello World</div>
const person = {
name: "John",
state: "LA",
};
console.log("Age", person.age); // error
console.log("Age", person.age ? person.age : "Not available"); // correct
console.log("Age", person.age ?? "Not available"); //correct
const oddNumbers = undefined;
console.log(oddNumbers.length); // error
console.log(oddNumbers.length ? oddNumbers.length : "Array is undefined"); // correct
console.log(oddNumbers.length ?? "Array is undefined"); // correct
const text = <div style={{ fontWeight: "bold" }}>Happy Learning!</div>; // bad approach
const text = <div className="learning-text">Happy Learning!</div>; // good approach
.learning-text {
font-weight: bold;
}
<div id="error-msg">Please enter a valid value</div>
document.getElementById("error-msg").visibility = visible;
const [isValid, setIsValid] = useState(false);
<div hidden={isValid}>Please enter a valid value</div>;
const printHello = () => console.log("HELLO");
useEffect(() => {
document.addEventListener("click", printHello);
return () => document.removeEventListener("click", printHello);
});
const Input=(props)=>{
const [inputValue, setInputValue]=useState('');
return(
<label>{props.thing}</label>
<input type='text' value={inputValue} onChange={(e)=>setInputValue(e.target.value)} />
)
}
// Import Input and use instead of JSX
<div>
<Input thing="First Name" />
<Input thing="Second Name" />
</div>
// Bad approach
if (name === "Ali") {
return 1;
} else if (name === "Bilal") {
return 2;
} else {
return 3;
}
// Good approach
name === "Ali" ? 1 : name === "Bilal" ? 2 : 3;
// If you have a file named index.js in a directory named actions and you want to import action from it in your component, your import would be like this
import { pageName } from "src/components/pages/index"; // Bad
import { pageName } from "src/components/pages"; // Good
// Bad approach
const Details = (props) => {
return (
<div>
<p>{props.name}</p>
<p>{props.age}</p>
<p>{props.designation}</p>
</div>
);
};
// Good approach
const Details = ({ name, age, designation }) => {
return (
<div>
<p>{name}</p>
<p>{age}</p>
<p>{designation}</p>
</div>
);
};
// In a function, if you are assigning a value to a state variable then you won't be able to access that assigned value even after it has been assigned in that function
const Message = () => {
const [message, setMessage] = useState("Hello World");
const changeMessage = (messageText) => {
setMessage("Happy Learning");
console.log(message); // It will print Hello World on console
};
return <div>{message}</div>;
};
// While comparing two values, strictly checking both values and their data types is a good practice.
"2" == 2 ? true : false; // true
"2" === 2 ? true : false; // false
function nextBiggest(arr) {
let max = 0,
result = 0;
for (const value of arr) {
const nr = Number(value);
if (nr > max) {
[result, max] = [max, nr]; // save previous max
} else if (nr < max && nr > result) {
result = nr; // new second biggest
}
}
return result;
}
const arr = ["20", "120", "111", "215", "215", "215", "54", "78"];
console.log(nextBiggest(arr));
(async () => {
await asyncCodeHere();
})();
<Suspense fallback={<Loading />}>
<SomeComponent />
</Suspense>
<!-- Will clamp the text to max line <3> or give value -->
<div className="line-clamp-3">text</div>
<!-- Will clamp the text to max 1 line -->
<div className="truncate">text</div>
<!-- put space between children without flex box -->
<div className="divide-y-8">
<div className="size-8"></div>
<div className="size-8"></div>
<div className="size-8"></div>
</div>
<!-- gradient -->
<div
className="h-48 w-full bg-gradient-to-r from-red-500 to-green-500 via-white from-20%"
></div>
<!-- button focus rings -->
<button className="size-8 bg-red-500 ring-4 ring-green-500">Click</button>