- About
- Props
- State
- UseRef
- UseEffect
- ConditionalRendering
- CreatePortal
- HTTP-Requests
- UseContext
- Reducer
- ReactRouter
- Redux
- Testing
React is a Javascript Library used to craft modern day UI.
- Airbnb
- Cloudflare
- Dropbox
- React App gets sent over from server
- Everything is downloaded unlike html where everything is downloaded when needed
- React has many ways to do a thing in constrast to Angular
- Other famous frameworks: Angular, Vue
npx create-react-app name-of-app
npm install
- className="myClass"
- Only one parent can be returned in JSX component
- Html cannot read javascript objects
- Wrap javascript in curly braces. E.g. {variableName}
- Each js file should only hold one functional component
- Component naming convention: CamelCase
- Props are properties that can be passed down to a child component from a parent component
- Props is a javascript object
- Each prop is a key-value pair
- Props passing is unidirectional (Parent -> Child)
- Example usage:
Parent:Child:const Parent = () => { return ( <Child title="hello" /> ) }
const Child = (props) => { return ( <div>{props.title}</div> ) }
import { useState } from "react"
Using array destructuring:
const [state, setState] = useState(initialValue);
- Component rerenders when the function setState is called
- Rerendering happens only after changing of state
- Console.log does not log the correct values of state
const [state, setState] = useState(initialValue);
const handleInput = (event) => {
setState(event.target.value);
}
return (
<input value={state} onChange={handleInput} />
)
import { useRef } from "react"
- Does not rerender when executed, might rerender
const reference = useRef(initialValue);
<input ref={reference} />
const focus = () => {reference.current.focus()} // this changes focus to the input element
- componentDidMount()
- componentDidUpdate()
- componentWillUnmount()
- useEffect(() => {}, [])
- useEffect(() => {})
- useEffect(() => { return () => {} })
- Shortcircuiting of logic:
{!{isLoading} && {displayComponent}}
- Ternary Operator:
{isLoading ? <LoadingSpinner /> : {displayComponent}}
- Logical AND (&&) return false when it hits a false-y value and return last value if all earlier values are truth-y
- Logical OR (||) continues evaluating when it hits a false-y value until it hits a truth-y value
import ReactDOM from "react-dom"
- create portal takes in JSX component followed by where to put it
Error modal:
const OverLay = () => {}
const mainFunctionName => {
return (
<>
{ReactDOM.createPortal(
<Overlay />, document.querySelector("#modal-root")
)}
</>
)
}
try {
const res = await fetch(url);
if (res.status !== 200) {
throw new Error("Something went wrong.") // throw will go to catch
}
const data = await res.json();
console.log(data);
} catch (err) {
setError(err.message);
}
useEffect(() => {
const url = "https://jsonplaceholder.typicode.com/posts/" + selection;
const controller = new AbortController();
fetchPost(url, controller.signal);
return () => {
controller.abort();
};
}, [selection]);
- useContext is used to pass props easily as opposed to the usual props drilling
- Danger of useContext is that everything within the context provider gets rerendered when states changes
- Create a context file:
import { createContext } from "react";
const MyContext = createContext();
export default MyContext;
- Import context and wrap the components:
import MyContext from "./MyContext";
const MyComponent = () => {
const var1 = "1234"
const var2 = "abcd"
return (
<MyContext.Provider value={var1, var2}>
<ChildComponent />
</MyContext.Provider>
)
}
- Access values in child component:
import MyContext from "./MyContext";
import { useContext } from "react";
const ChildComponent = () => {
const myContext = useContext(MyContext);
return (
<>
<div>{myContext.var1}</div>
<div>{myContext.var2}</div>
</>
)
}
Similar to useState, reducer is used to deal with more complex state structures such as array and javascript objects or complex logic
import { useState } from "react";
const [state, setState] = useState(initialValue);
const reducer = (state, action) => {
switch (action) {
case "increase":
return state + 1;
break;
case "decrease":
return state - 1;
break;
case "reset":
return 0
break;
default:
return state;
}
}
<button onClick={setState(reducer(state, "increase"))}>Increase</button>
<button onClick={setState(reducer(state, "decrease"))}>Decrease</button>
<button onClick={setState(reducer(state, "reset"))}>Reset</button>
import { useReducer } from "react";
const [state, dispatch] = useReducer(reducer, initialValue);
const reducer = (state, action) => {
switch (action.type) {
case "increase":
return state + action.value;
break;
case "decrease":
return state - action.value;
break;
case "reset":
return action.value
break;
default:
return state;
}
}
return (
<button onClick={dispatch({type: "increase", value: 1})}>Increase</button>
<button onClick={dispatch({type: "decrease", value: 1})}>Decrease</button>
<button onClick={dispatch({type: "reset", value: 0})}>Reset</button>
);
- Install router:
npm i react-router-dom@5
- Set up router in index.js:
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import { BrowserRouter } from "react-router-dom";
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById("root")
);
- Set up routes in your component:
- import Route
- import Pages
- set route paths to wrap each page
import React from "react";
import { Route, Redirect, Switch } from "react-router-dom";
import NavBar from "./components/NavBar";
import Details from "./pages/Details";
import Main from "./pages/Main";
import PageOne from "./pages/PageOne";
import PageThree from "./pages/PageThree";
import PageTwo from "./pages/PageTwo";
function App() {
return (
<div className="container">
<NavBar />
<Switch>
<Route exact path="/">
<Redirect to="/page-one" />
</Route>
<Route exact path="/page-one">
<PageOne></PageOne>
</Route>
<Route path="/page-two">
<PageTwo></PageTwo>
</Route>
<Route path="/page-one/:item">
<Details />
</Route>
<Route path="/page-three">
<PageThree></PageThree>
</Route>
</Switch>
</div>
);
}
export default App;
- Use links to link to your pages
import { Link } from "react-router-dom";
<Link to="/">Main</Link>
<Link to="/page-one">Page One</Link>
<Link to="/page-two">Page Two</Link>
<Link to="/page-three">Page Three</Link>
- React router dom 5 use greedy search for path so it will always display the main page
- To fix the issue where only main page is shown is to use exact keyword as seen in "3. Set up routes"
- Using switch ensures that only one page is displayed at a time, however does not eliminate the greedy search for path problem:
import { Switch } from "react-router-dom";
<Switch>
<Route exact path="/">
<Main />
</Route>
<Route path="/page-one">
<PageOne />
</Route>
</Switch>
- Install latest react router:
npm i react-router-dom
- Example:
const App = () => {
return (
<div className="container">
<NavBar />
<Routes>
<Route path="/" element={<Navigate replace to="/page-one" />} />
<Route path="/page-one" element={<PageOne />} />
<Route path="/page-two" element={<PageTwo />} />
<Route path="/page-one/:item" element={<Details />} />
<Route path="/page-three" element={<PageThree />} />
</Routes>
</div>
);
}
export default App;
- Use NavLinks to link to pages:
import { NavLink } from "react-router-dom";
<NavLink to="/">Main</NavLink>
<NavLink to="/page-one">Page One</NavLink>
<NavLink to="/page-two">Page Two</NavLink>
<NavLink to="/page-three">Page Three</NavLink>
- Pretty new, might not be implemented in many companies
- No need for Switch
- No need for exact keyword
- Wrap all route in
<Routes>
- NavLink instead of Link
- Navigate instead of redirect
- Install Redux Toolkit
npm install @reduxjs/toolkit
- Create Slice Reducers and Actions AKA
createSlice
- import
createSlice
- "mutating" logic can be used in
createSlice
- choose to create "selector" functions
import { createSlice } from "@reduxjs/toolkit";
const counterSlice = createSlice({
name: "counter",
initialState: {
value: 0
},
reducers: {
increment: state => {
state.value += 1
},
decrement: state => {
state.value -= 1
},
incrementByAmount: (state, action) => {
state.value += action.payload
}
}
})
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
- Create Redux Store AKA
configureStore
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "../features/counter/counterSlice"
const store = configureStore({
reducer: {
counter: counterReducer,
},
});
export default store;
- Create Component
- Read data with
useSelector
- Change store state using
useDispatch
import { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { decrement, increment, incrementByAmount } from "./counterSlice";
const myComponent = () => {
const dispatch = useDispatch();
const count = useSelector((state) => state.value);
return (
<>
<div>Current value: {count}</div>
<button onClick={() => dispatch(increment())}>+</>
<button onClick={() => dispatch(decrement())}>-</>
</>
)
}
- Provide the Store
import ReactDOM from "react-dom"
import React from "react"
import { Provider } from "react-redux"
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)