convert line endings to LF

This commit is contained in:
Marcello 2024-06-25 14:14:51 +02:00
parent b74f634cd2
commit d9d8d55730
Signed by: m-lamonaca
SSH key fingerprint: SHA256:8db8uii6Gweq7TbKixFBioW2T8CbgtyFETyYL3cr3zk
9 changed files with 3257 additions and 3257 deletions

View file

@ -1,103 +1,103 @@
# [React Router](https://reactrouter.com)
Popular routing library. Allows to specify a route through React components, declaring which component is to be loaded for a given URL.
Key Components:
- **Router**: wrap the app entry-point, usually `BrowserRouter`
- **Route**: "Load this component for this URL"
- **Link**: react-managed anchors that won't post back to the browser
## Routers
Router Types:
- *HashRouter*: `#route`, adds hashes to the URLs
- *BrowserRouter*: `/route`, uses HTML5 history API to provide clean URLs
- *MemoryRouter*: no URL
```js
// index.js
//other imports ...
import { BrowserRouter as Router } from "react-router-dom";
React.render(
<Router>
<App />
</Router>,
document.getElementById("DomID");
)
```
```js
// Component.js
import { Route, Route } from "react-router-dom";
<div>
{/* match route pattern exactly, all sub-routes will be matched otherwise */}
<Route path="/" exact element={<Component props={props} />} />
<Route path="/route" element={<Component props={props} />} />
...
</div>
// only one child can match, similar to Route-case
<Routes>
<Route path="/" exact element={<Component props={props} />} />
<Route path="/route" element={<Component props={props} />} />
<Route component={PageNotFound} /> {/* matches all non-existent URLs */}
</Route>
```
### URL Parameters & Query String
```js
// Given
<Route path="/route/:placeholder" element={<Component props={props} />} />
// URL: app.com/route/sub-route?param=value
function Component(props) {
props.match.params.placeholder; // sub-route
props.location.query; // { param: value }
props.location.pathname; // /route/sub-route?param=value
}
```
### Redirecting
```js
import { Navigate } from "react-router-dom";
// redirects to another URL on render, shouldn't be rendered on component mount but after an action
<Navigate to="/route" />
<Navigate from="/old-route" to="/new-route" />
{ condition && <Navigate to="/route" /> } // redirect if condition is true
// or redirect manipulating the history (always in props)
props.history.push("/new-route");
```
### Prompts
```js
import { Prompt } from "react-router-dom";
// displays a prompt when the condition is true
<Prompt when={condition} message="prompt message" />
```
## Link
Clicks on a link created with React-Router will be captured by react an all the routing will happen client side.
```js
import { Link } from "react-router-dom";
// TARGET: <Route path="/route/:itemId" />
<Link to="/route/1">Text</Link>
// add styling attributes to the rendered element when it matches the current URL.
<NavLink to="/route" exact activeClassName="class">Text</NavLink>
<NavLink to="/route" activeStyle={ { cssProp: value } }>Text</NavLink>
```
# [React Router](https://reactrouter.com)
Popular routing library. Allows to specify a route through React components, declaring which component is to be loaded for a given URL.
Key Components:
- **Router**: wrap the app entry-point, usually `BrowserRouter`
- **Route**: "Load this component for this URL"
- **Link**: react-managed anchors that won't post back to the browser
## Routers
Router Types:
- *HashRouter*: `#route`, adds hashes to the URLs
- *BrowserRouter*: `/route`, uses HTML5 history API to provide clean URLs
- *MemoryRouter*: no URL
```js
// index.js
//other imports ...
import { BrowserRouter as Router } from "react-router-dom";
React.render(
<Router>
<App />
</Router>,
document.getElementById("DomID");
)
```
```js
// Component.js
import { Route, Route } from "react-router-dom";
<div>
{/* match route pattern exactly, all sub-routes will be matched otherwise */}
<Route path="/" exact element={<Component props={props} />} />
<Route path="/route" element={<Component props={props} />} />
...
</div>
// only one child can match, similar to Route-case
<Routes>
<Route path="/" exact element={<Component props={props} />} />
<Route path="/route" element={<Component props={props} />} />
<Route component={PageNotFound} /> {/* matches all non-existent URLs */}
</Route>
```
### URL Parameters & Query String
```js
// Given
<Route path="/route/:placeholder" element={<Component props={props} />} />
// URL: app.com/route/sub-route?param=value
function Component(props) {
props.match.params.placeholder; // sub-route
props.location.query; // { param: value }
props.location.pathname; // /route/sub-route?param=value
}
```
### Redirecting
```js
import { Navigate } from "react-router-dom";
// redirects to another URL on render, shouldn't be rendered on component mount but after an action
<Navigate to="/route" />
<Navigate from="/old-route" to="/new-route" />
{ condition && <Navigate to="/route" /> } // redirect if condition is true
// or redirect manipulating the history (always in props)
props.history.push("/new-route");
```
### Prompts
```js
import { Prompt } from "react-router-dom";
// displays a prompt when the condition is true
<Prompt when={condition} message="prompt message" />
```
## Link
Clicks on a link created with React-Router will be captured by react an all the routing will happen client side.
```js
import { Link } from "react-router-dom";
// TARGET: <Route path="/route/:itemId" />
<Link to="/route/1">Text</Link>
// add styling attributes to the rendered element when it matches the current URL.
<NavLink to="/route" exact activeClassName="class">Text</NavLink>
<NavLink to="/route" activeStyle={ { cssProp: value } }>Text</NavLink>
```

View file

@ -1,261 +1,261 @@
# React
## Components
There are two types of react components:
- Function Components
- Class Components
Both types can be stateful and have side effects or be purely presentational.
```jsx
// functional component
const Component = (props) => {
return (
<domElementOrComponent... />
);
}
// class component
class Component extends React.Component {
return (
<domElementOrComponent... />
);
}
```
*NOTE*: a component name *must* start with an uppercase letter.
Every components has two inputs: *props* and *state*. The props input is explicit while the state is implicit.
State is used to determine the changes and when to re-render.
Within the component state can be changed while the props object represent fixed input values.
JSX syntax can represent HTML but gets converted to pure JavaScript before being sent to the browser:
```js
// JSX
const element = (
<h1 className="greeting">Hello, world!</h1>
);
// compiled JS shipped to browser
const element = React.createElement(
'h1', // HTML tag name
{className: 'greeting'}, // attrs as JSON
'Hello, world!' // tag content (can be nested component)
);
```
### App Entry-point
```js
const container = document.getElementById('root')!;
const root = createRoot(container);
const element = <h1s>Hello World</h1>
root.render(element)
```
### Dynamic Expressions
```js
<tag>{expression}</tag> // expression is evaluated an it's result is displayed
<tag onEvent={funcReference}>{expression}</tag>
<tag onEvent={() => func(args)}>{expression}</tag>
```
### Props
```js
<Component propName={value} /> // pass a value the component
<Component propName={funcReference} /> // pass a function to the component
function Component(props) {
// use props with {props.propName}
}
class Component extends React.Component{
// use props with {this.props.propName}
render()
}
```
### Simple Function Component
```js
// Button.js
import { useState } from "react";
function Button() {
const [count, setCount] = useState(0); // hook
const handleCLick = () => setCount(count + 1); // logic
// JSX
return (
<button onClick={handleCLick}>
{count}
</button>
);
}
export default Button;
```
### Simple Class Component
```js
class Button extends React.Component {
state = {count: 0};
//or
constructor(props) {
super(props);
this.state = {count: 0};
}
componentDidMount() {} // called on successful component mount
handleClick = () => {
this.setState({ count: this.state.count + 1 });
}
// or
handleClick = () => {
this.setState((state, props) => ({ count: state.count + props.increment }) );
}
render(){
return (
<button onClick={this.handleClick}>
{this.state.count}
</button>
);
}
}
```
### Nesting Components
```js
import { useState } from "react";
function Button(props) {
return (
<button onClick={props.onClickFunc}>
+1
</button>
);
}
function Display (props) {
return (
<div>{props.message}</div>
);
}
function App() {
// state must be declare in the outer component it can be passed to each children
const [count, setCount] = useState(0);
const incrementCounter = () => setCount(count + 1);
return (
<div className="App">
<Button onClickFunc={incrementCounter}/>
<Display message={count}/>
</div>
);
}
export default App;
```
### User Input (Forms)
```js
function Form() {
const [userName, setUserName] = useState("");
handleSubmit = (event) => {
event.preventDefault();
// ...
}
return(
<form onSubmit={handleSubmit}>
<input
type="text"
value={userName} // controlled component
onChange={(event) => setUserName(event.target.value)} // needed to update UI on dom change
required
/>
<button></button>
</form>
);
}
```
### Lists of Components
```js
// ...
<div>
{array.map(item => <Component key={uniqueID}>)}
</div>
// ...
```
> **Note**: The `key` attribute of the component is needed to identify a particular item. It's most useful if the list has to be sorted.
## Hooks
### `useState`
Hook used to create a state object.
`useState()` results:
- state object (getter)
- updater function (setter)
```js
const [state, setState] = useState(default);
```
### `useEffect`
Hook used to trigger an action on each render of the component or when one of the watched items changes.
```js
useEffect(() => {
// "side effects" operations
return () => {/* clean up side effect */} // optional
}, [/* list of watched items, empty triggers once */]);
```
### Custom Hooks
```js
// hook definitions
const useCustomHook = () => {
// eventual state definitions
// eventual function definitions
// ...
return { obj1, obj2, ... };
}
const Component(){
// retrieve elements from the hook
const {
obj1,
obj2,
...
} = useCustomHook();
}
```
# React
## Components
There are two types of react components:
- Function Components
- Class Components
Both types can be stateful and have side effects or be purely presentational.
```jsx
// functional component
const Component = (props) => {
return (
<domElementOrComponent... />
);
}
// class component
class Component extends React.Component {
return (
<domElementOrComponent... />
);
}
```
*NOTE*: a component name *must* start with an uppercase letter.
Every components has two inputs: *props* and *state*. The props input is explicit while the state is implicit.
State is used to determine the changes and when to re-render.
Within the component state can be changed while the props object represent fixed input values.
JSX syntax can represent HTML but gets converted to pure JavaScript before being sent to the browser:
```js
// JSX
const element = (
<h1 className="greeting">Hello, world!</h1>
);
// compiled JS shipped to browser
const element = React.createElement(
'h1', // HTML tag name
{className: 'greeting'}, // attrs as JSON
'Hello, world!' // tag content (can be nested component)
);
```
### App Entry-point
```js
const container = document.getElementById('root')!;
const root = createRoot(container);
const element = <h1s>Hello World</h1>
root.render(element)
```
### Dynamic Expressions
```js
<tag>{expression}</tag> // expression is evaluated an it's result is displayed
<tag onEvent={funcReference}>{expression}</tag>
<tag onEvent={() => func(args)}>{expression}</tag>
```
### Props
```js
<Component propName={value} /> // pass a value the component
<Component propName={funcReference} /> // pass a function to the component
function Component(props) {
// use props with {props.propName}
}
class Component extends React.Component{
// use props with {this.props.propName}
render()
}
```
### Simple Function Component
```js
// Button.js
import { useState } from "react";
function Button() {
const [count, setCount] = useState(0); // hook
const handleCLick = () => setCount(count + 1); // logic
// JSX
return (
<button onClick={handleCLick}>
{count}
</button>
);
}
export default Button;
```
### Simple Class Component
```js
class Button extends React.Component {
state = {count: 0};
//or
constructor(props) {
super(props);
this.state = {count: 0};
}
componentDidMount() {} // called on successful component mount
handleClick = () => {
this.setState({ count: this.state.count + 1 });
}
// or
handleClick = () => {
this.setState((state, props) => ({ count: state.count + props.increment }) );
}
render(){
return (
<button onClick={this.handleClick}>
{this.state.count}
</button>
);
}
}
```
### Nesting Components
```js
import { useState } from "react";
function Button(props) {
return (
<button onClick={props.onClickFunc}>
+1
</button>
);
}
function Display (props) {
return (
<div>{props.message}</div>
);
}
function App() {
// state must be declare in the outer component it can be passed to each children
const [count, setCount] = useState(0);
const incrementCounter = () => setCount(count + 1);
return (
<div className="App">
<Button onClickFunc={incrementCounter}/>
<Display message={count}/>
</div>
);
}
export default App;
```
### User Input (Forms)
```js
function Form() {
const [userName, setUserName] = useState("");
handleSubmit = (event) => {
event.preventDefault();
// ...
}
return(
<form onSubmit={handleSubmit}>
<input
type="text"
value={userName} // controlled component
onChange={(event) => setUserName(event.target.value)} // needed to update UI on dom change
required
/>
<button></button>
</form>
);
}
```
### Lists of Components
```js
// ...
<div>
{array.map(item => <Component key={uniqueID}>)}
</div>
// ...
```
> **Note**: The `key` attribute of the component is needed to identify a particular item. It's most useful if the list has to be sorted.
## Hooks
### `useState`
Hook used to create a state object.
`useState()` results:
- state object (getter)
- updater function (setter)
```js
const [state, setState] = useState(default);
```
### `useEffect`
Hook used to trigger an action on each render of the component or when one of the watched items changes.
```js
useEffect(() => {
// "side effects" operations
return () => {/* clean up side effect */} // optional
}, [/* list of watched items, empty triggers once */]);
```
### Custom Hooks
```js
// hook definitions
const useCustomHook = () => {
// eventual state definitions
// eventual function definitions
// ...
return { obj1, obj2, ... };
}
const Component(){
// retrieve elements from the hook
const {
obj1,
obj2,
...
} = useCustomHook();
}
```

View file

@ -1,465 +1,465 @@
# [Redux](https://redux.js.org/)
Redux is a pattern and library for managing and updating application state, using events called *actions*. It serves as a centralized store for state that needs to be used across the entire application, with rules ensuring that the state can only be updated in a predictable fashion.
## Actions, Store, Immutability & Reducers
### Actions & Action Creators
An **Action** is a plain JavaScript object that has a `type` field. An action object can have other fields with additional information about what happened.
By convention, that information is stored in a field called `payload`.
**Action Creators** are functions that create and return action objects.
```js
function actionCreator(data)
{
return { type: ACTION_TYPE, payload: data }; // action obj
}
```
### Store
The current Redux application state lives in an object called the **store**.
The store is created by passing in a reducer, and has a method called `getState` that returns the current state value.
The Redux store has a method called `dispatch`. The only way to update the state is to call `store.dispatch()` and pass in an action object.
The store will run its reducer function and save the new state value inside.
**Selectors** are functions that know how to extract specific pieces of information from a store state value.
In `initialState.js`;
```js
export default {
// initial state here
}
```
In `configStore.js`:
```js
// configStore.js
import { createStore, applyMiddleware, compose } from "redux";
export function configStore(initialState) {
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; // support for redux devtools
return createStore(
rootReducer,
initialState,
composeEnhancers(applyMiddleware(middleware, ...))
);
}
// available functions & methods
replaceReducer(newReducer); // replace an existing reducer, useful for Hot Reload
store.dispatch(action); // trigger a state change based on an action
store.subscribe(listener);
store.getState(); // retrieve current state
```
### Reducers
**Reducers** are functions that receives the current state and an action, decide how to update the state if necessary, and return the new state.
Reducers must **always** follow some specific rules:
- They should only calculate the new state value based on the `state` and `action` arguments
- They are not allowed to modify the existing `state`.
Instead, they must make *immutable updates*, by copying the existing `state` and making changes to the copied values.
- They must not do any asynchronous logic, calculate random values, or cause other "side effects"
```js
import initialState from "path/to/initialState";
function reducer(state = initialState, action) {
switch(action.type){
case "ACTION_TYPE":
return { ...state, prop: value }; // return modified copy of state (using spread operator)
break;
default:
return state; // return unchanged state (NEEDED)
}
}
// combining reducers
import { combineReducers } from "redux";
const rootReducer = combineReducers({
entity: entityReducer.
...
});
```
> **Note**: multiple reducers can be triggered by the same action since each one operates on a different portion of the state.
## [React-Redux](https://react-redux.js.org/)
### Container vs Presentational Components
Container Components:
- Focus on how thing work
- Aware of Redux
- Subscribe to Redux State
- Dispatch Redux actions
Presentational Components:
- Focus on how things look
- Unaware of Redux
- Read data from props
- Invoke callbacks on props
### Provider Component & Connect
Used at the root component and wraps all the application.
```js
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { configStore } from 'path/to/configStore';
import initialState from "path/to/initialState";
import App from './App';
const store = configStore(initialState);
const rootElement = document.getElementById('root');
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement
);
```
```js
// Component.js
import { connect } from 'react-redux';
import { increment, decrement, reset } from './actionCreators';
// const Component = ...
// specifies which state is passed to the component (called on state change)
const mapStateToProps = (state, ownProps /* optional */) => {
// structure of the props passed to the component
return { propName: state.property };
};
// specifies the action passed to a component (the key is the name that the prop will have )
const mapDispatchToProps = { actionCreator: actionCreator };
// or
function mapDispatchToProps(dispatch) {
return {
// wrap action creators
actionCreator: (args) => dispatch(actionCreator(args))
};
}
// or
function mapDispatchToProps(dispatch) {
return {
actionCreator: bindActionCreators(actionCreator, dispatch),
actions: bindActionCreators(allActionCreators, dispatch)
};
}
// both args are optional
// if mapDispatch is missing the dispatch function is added to the props
export default connect(mapStateToProps, mapDispatchToProps)(Component);
```
## Async Operations with [Redux-Thunk](https://github.com/reduxjs/redux-thunk)
**Note**: Redux middleware runs *after* and action and *before* it's reducer.
Redux-Thunk allows to return functions instead of objects from action creators.
A "thunk" is a function that wraps an expression to delay it's evaluation.
In `configStore.js`:
```js
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
function configStore(initialState) {
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; // support for redux devtools
return createStore(
rootReducer,
initialState,
composeEnhancers(applyMiddleware(thunk, ...)) // add thunks middleware
);
}
```
```js
// usually action on async func success
function actionCreator(arg) {
return { type: TYPE, data: arg };
}
export function thunk() {
return function (dispatch) { // redux-thunk injects dispatch as arg
return asyncFunction().then((data) => { // async function returns a promise
dispatch(actionCreator(data));
})
.catch((error) => {
throw error;
});
};
}
// or using async/await
export async function thunk() {
return function (dispatch) { // redux-thunk injects dispatch as arg
try {
let data = await asyncFunction();
return dispatch(actionCreator(data));
} catch(error) {
throw error;
}
}
}
```
## [Redux-Toolkit](https://redux-toolkit.js.org/)
The Redux Toolkit package is intended to be the standard way to write Redux logic. It was originally created to help address three common concerns about Redux.
Redux Toolkit also includes a powerful data fetching and caching capability dubbed "RTK Query". It's included in the package as a separate set of entry points. It's optional, but can eliminate the need to hand-write data fetching logic yourself.
These tools should be beneficial to all Redux users. Whether you're a brand new Redux user setting up your first project, or an experienced user who wants to simplify an existing application, Redux Toolkit can help you make your Redux code better.
Installation
Using Create React App
The recommended way to start new apps with React and Redux is by using the official Redux+JS template or Redux+TS template for Create React App, which takes advantage of Redux Toolkit and React Redux's integration with React components.
```sh
# Redux + Plain JS template
npx create-react-app my-app --template redux
# Redux + TypeScript template
npx create-react-app my-app --template redux-typescript
```
Redux Toolkit includes these APIs:
- [`configureStore()`][cfg_store]: wraps `createStore` to provide simplified configuration options and good defaults.
It can automatically combines slice reducers, adds whatever Redux middleware supplied, includes redux-thunk by default, and enables use of the Redux DevTools Extension.
- [`createReducer()`][new_reducer]: that lets you supply a lookup table of action types to case reducer functions, rather than writing switch statements.
In addition, it automatically uses the `immer` library to let you write simpler immutable updates with normal mutative code, like `state.todos[3].completed = true`.
- [`createAction()`][new_action]: generates an action creator function for the given action type string.
The function itself has `toString()` defined, so that it can be used in place of the type constant.
- [`createSlice()`][new_slice]: accepts an object of reducer functions, a slice name, and an initial state value, and automatically generates a slice reducer with corresponding action creators and action types.
- [`createAsyncThunk`][new_async_thunk]: accepts an action type string and a function that returns a promise, and generates a thunk that dispatches pending/fulfilled/rejected action types based on that promise
- [`createEntityAdapter`][entity_adapt]: generates a set of reusable reducers and selectors to manage normalized data in the store
- The `createSelector` utility from the Reselect library, re-exported for ease of use.
[cfg_store]: https://redux-toolkit.js.org/api/configureStore
[new_reducer]: https://redux-toolkit.js.org/api/createReducer
[new_action]: https://redux-toolkit.js.org/api/createAction
[new_slice]: https://redux-toolkit.js.org/api/createSlice
[new_async_thunk]: https://redux-toolkit.js.org/api/createAsyncThunk
[entity_adapt]: https://redux-toolkit.js.org/api/createEntityAdapter
### [`configureStore`](https://redux-toolkit.js.org/api/configureStore)
Included Default Middleware:
- Immutability check middleware: deeply compares state values for mutations. It can detect mutations in reducers during a dispatch, and also mutations that occur between dispatches.
When a mutation is detected, it will throw an error and indicate the key path for where the mutated value was detected in the state tree. (Forked from `redux-immutable-state-invariant`.)
- Serializability check middleware: a custom middleware created specifically for use in Redux Toolkit
Similar in concept to `immutable-state-invariant`, but deeply checks the state tree and the actions for non-serializable values such as functions, Promises, Symbols, and other non-plain-JS-data values
When a non-serializable value is detected, a console error will be printed with the key path for where the non-serializable value was detected.
- In addition to these development tool middleware, it also adds `redux-thunk` by default, since thunks are the basic recommended side effects middleware for Redux.
Currently, the return value of `getDefaultMiddleware()` is:
```js
// development
const middleware = [thunk, immutableStateInvariant, serializableStateInvariant]
// production
const middleware = [thunk]
```
```js
import { combineReducers } from 'redux'
import { configureStore } from '@reduxjs/toolkit'
import monitorReducersEnhancer from './enhancers/monitorReducers'
import loggerMiddleware from './middleware/logger'
import usersReducer from './usersReducer'
import postsReducer from './postsReducer'
const rootReducer = combineReducers({
users: usersReducer,
posts: postsReducer,
})
const store = configureStore({
// reducers combined automatically
reducer: rootReducer,
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(loggerMiddleware),
enhancers: [monitorReducersEnhancer]
})
export default store
```
### [`createAction`](https://redux-toolkit.js.org/api/createAction)
```js
import { createAction } from '@reduxjs/toolkit';
const increment = createAction<number | undefined>('counter/increment');
const action = increment(); // { type: 'counter/increment' }
const action = increment(3); // { type: 'counter/increment', payload: 3 }
increment.toString(); // 'counter/increment'
```
### [`createReducer`](https://redux-toolkit.js.org/api/createReducer)
```js
import { createAction, createReducer } from '@reduxjs/toolkit'
interface CounterState {
value: number
}
const increment = createAction('counter/increment')
const decrement = createAction('counter/decrement')
const incrementByAmount = createAction<number>('counter/incrementByAmount')
const initialState = { value: 0 } as CounterState
const counterReducer = createReducer(initialState, (builder) => {
builder
.addCase(increment, (state, action) => {
state.value++
})
.addCase(decrement, (state, action) => {
state.value--
})
.addCase(incrementByAmount, (state, action) => {
state.value += action.payload
})
})
```
### [`createSlice`](https://redux-toolkit.js.org/api/createSlice)
A function that accepts an initial state, an object of reducer functions, and a "slice name", and automatically generates action creators and action types that correspond to the reducers and state.
Internally, it uses `createAction` and `createReducer`, so it's possible to use Immer to write "mutating" immutable updates.
**Note**: action types will have the `<slice-name>/<reducer-name>` shape.
```js
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
interface CounterState {
value: number
}
const initialState = { value: 0 } as CounterState
const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment(state) {
state.value++
},
decrement(state) {
state.value--
},
incrementByAmount(state, action: PayloadAction<number>) {
state.value += action.payload
},
},
})
export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducer
```
### [`createAsyncThunk`](https://redux-toolkit.js.org/api/createAsyncThunk)
The function `createAsyncThunk` returns a standard Redux thunk action creator.
The thunk action creator function will have plain action creators for the pending, fulfilled, and rejected cases attached as nested fields.
The `payloadCreator` function will be called with two arguments:
- `arg`: a single value, containing the first parameter that was passed to the thunk action creator when it was dispatched.
- `thunkAPI`: an object containing all of the parameters that are normally passed to a Redux thunk function, as well as additional options:
- `dispatch`: the Redux store dispatch method
- `getState`: the Redux store getState method
- `extra`: the "extra argument" given to the thunk middleware on setup, if available
- `requestId`: a unique string ID value that was automatically generated to identify this request sequence
- `signal`: an `AbortController.signal` object that may be used to see if another part of the app logic has marked this request as needing cancellation.
- [...]
The logic in the `payloadCreator` function may use any of these values as needed to calculate the result.
```js
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
const payloadCreator = async (arg, ThunkAPI): Promise<T> => { /* ... */ };
const thunk = createAsyncThunk("<action-type>", payloadCreator);
thunk.pending; // action creator that dispatches an '<action-type>/pending'
thunk.fulfilled; // action creator that dispatches an '<action-type>/fulfilled'
thunk.rejected; // action creator that dispatches an '<action-type>/rejected'
const slice = createSlice({
name: '<action-name>',
initialState,
reducers: { /* standard reducer logic, with auto-generated action types per reducer */ },
extraReducers: (builder) => {
// Add reducers for additional action types here, and handle loading state as needed
builder.addCase(thunk.fulfilled, (state, action) => { /* body of the reducer */ })
},
})
```
## RTK Query
RTK Query is provided as an optional addon within the `@reduxjs/toolkit` package.
It is purpose-built to solve the use case of data fetching and caching, supplying a compact, but powerful toolset to define an API interface layer got the app.
It is intended to simplify common cases for loading data in a web application, eliminating the need to hand-write data fetching & caching logic yourself.
RTK Query is included within the installation of the core Redux Toolkit package. It is available via either of the two entry points below:
```cs
import { createApi } from '@reduxjs/toolkit/query'
/* React-specific entry point that automatically generates hooks corresponding to the defined endpoints */
import { createApi } from '@reduxjs/toolkit/query/react'
```
RTK Query includes these APIs:
- [`createApi()`][new_api]: The core of RTK Query's functionality. It allows to define a set of endpoints describe how to retrieve data from a series of endpoints,
including configuration of how to fetch and transform that data.
- [`fetchBaseQuery()`][fetch_query]: A small wrapper around fetch that aims to simplify requests. Intended as the recommended baseQuery to be used in createApi for the majority of users.
- [`<ApiProvider />`][api_provider]: Can be used as a Provider if you do not already have a Redux store.
- [`setupListeners()`][setup_listener]: A utility used to enable refetchOnMount and refetchOnReconnect behaviors.
[new_api]: https://redux-toolkit.js.org/rtk-query/api/createApi
[fetch_query]: https://redux-toolkit.js.org/rtk-query/api/fetchBaseQuery
[api_provider]: https://redux-toolkit.js.org/rtk-query/api/ApiProvider
[setup_listener]: https://redux-toolkit.js.org/rtk-query/api/setupListeners
# [Redux](https://redux.js.org/)
Redux is a pattern and library for managing and updating application state, using events called *actions*. It serves as a centralized store for state that needs to be used across the entire application, with rules ensuring that the state can only be updated in a predictable fashion.
## Actions, Store, Immutability & Reducers
### Actions & Action Creators
An **Action** is a plain JavaScript object that has a `type` field. An action object can have other fields with additional information about what happened.
By convention, that information is stored in a field called `payload`.
**Action Creators** are functions that create and return action objects.
```js
function actionCreator(data)
{
return { type: ACTION_TYPE, payload: data }; // action obj
}
```
### Store
The current Redux application state lives in an object called the **store**.
The store is created by passing in a reducer, and has a method called `getState` that returns the current state value.
The Redux store has a method called `dispatch`. The only way to update the state is to call `store.dispatch()` and pass in an action object.
The store will run its reducer function and save the new state value inside.
**Selectors** are functions that know how to extract specific pieces of information from a store state value.
In `initialState.js`;
```js
export default {
// initial state here
}
```
In `configStore.js`:
```js
// configStore.js
import { createStore, applyMiddleware, compose } from "redux";
export function configStore(initialState) {
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; // support for redux devtools
return createStore(
rootReducer,
initialState,
composeEnhancers(applyMiddleware(middleware, ...))
);
}
// available functions & methods
replaceReducer(newReducer); // replace an existing reducer, useful for Hot Reload
store.dispatch(action); // trigger a state change based on an action
store.subscribe(listener);
store.getState(); // retrieve current state
```
### Reducers
**Reducers** are functions that receives the current state and an action, decide how to update the state if necessary, and return the new state.
Reducers must **always** follow some specific rules:
- They should only calculate the new state value based on the `state` and `action` arguments
- They are not allowed to modify the existing `state`.
Instead, they must make *immutable updates*, by copying the existing `state` and making changes to the copied values.
- They must not do any asynchronous logic, calculate random values, or cause other "side effects"
```js
import initialState from "path/to/initialState";
function reducer(state = initialState, action) {
switch(action.type){
case "ACTION_TYPE":
return { ...state, prop: value }; // return modified copy of state (using spread operator)
break;
default:
return state; // return unchanged state (NEEDED)
}
}
// combining reducers
import { combineReducers } from "redux";
const rootReducer = combineReducers({
entity: entityReducer.
...
});
```
> **Note**: multiple reducers can be triggered by the same action since each one operates on a different portion of the state.
## [React-Redux](https://react-redux.js.org/)
### Container vs Presentational Components
Container Components:
- Focus on how thing work
- Aware of Redux
- Subscribe to Redux State
- Dispatch Redux actions
Presentational Components:
- Focus on how things look
- Unaware of Redux
- Read data from props
- Invoke callbacks on props
### Provider Component & Connect
Used at the root component and wraps all the application.
```js
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { configStore } from 'path/to/configStore';
import initialState from "path/to/initialState";
import App from './App';
const store = configStore(initialState);
const rootElement = document.getElementById('root');
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement
);
```
```js
// Component.js
import { connect } from 'react-redux';
import { increment, decrement, reset } from './actionCreators';
// const Component = ...
// specifies which state is passed to the component (called on state change)
const mapStateToProps = (state, ownProps /* optional */) => {
// structure of the props passed to the component
return { propName: state.property };
};
// specifies the action passed to a component (the key is the name that the prop will have )
const mapDispatchToProps = { actionCreator: actionCreator };
// or
function mapDispatchToProps(dispatch) {
return {
// wrap action creators
actionCreator: (args) => dispatch(actionCreator(args))
};
}
// or
function mapDispatchToProps(dispatch) {
return {
actionCreator: bindActionCreators(actionCreator, dispatch),
actions: bindActionCreators(allActionCreators, dispatch)
};
}
// both args are optional
// if mapDispatch is missing the dispatch function is added to the props
export default connect(mapStateToProps, mapDispatchToProps)(Component);
```
## Async Operations with [Redux-Thunk](https://github.com/reduxjs/redux-thunk)
**Note**: Redux middleware runs *after* and action and *before* it's reducer.
Redux-Thunk allows to return functions instead of objects from action creators.
A "thunk" is a function that wraps an expression to delay it's evaluation.
In `configStore.js`:
```js
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
function configStore(initialState) {
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; // support for redux devtools
return createStore(
rootReducer,
initialState,
composeEnhancers(applyMiddleware(thunk, ...)) // add thunks middleware
);
}
```
```js
// usually action on async func success
function actionCreator(arg) {
return { type: TYPE, data: arg };
}
export function thunk() {
return function (dispatch) { // redux-thunk injects dispatch as arg
return asyncFunction().then((data) => { // async function returns a promise
dispatch(actionCreator(data));
})
.catch((error) => {
throw error;
});
};
}
// or using async/await
export async function thunk() {
return function (dispatch) { // redux-thunk injects dispatch as arg
try {
let data = await asyncFunction();
return dispatch(actionCreator(data));
} catch(error) {
throw error;
}
}
}
```
## [Redux-Toolkit](https://redux-toolkit.js.org/)
The Redux Toolkit package is intended to be the standard way to write Redux logic. It was originally created to help address three common concerns about Redux.
Redux Toolkit also includes a powerful data fetching and caching capability dubbed "RTK Query". It's included in the package as a separate set of entry points. It's optional, but can eliminate the need to hand-write data fetching logic yourself.
These tools should be beneficial to all Redux users. Whether you're a brand new Redux user setting up your first project, or an experienced user who wants to simplify an existing application, Redux Toolkit can help you make your Redux code better.
Installation
Using Create React App
The recommended way to start new apps with React and Redux is by using the official Redux+JS template or Redux+TS template for Create React App, which takes advantage of Redux Toolkit and React Redux's integration with React components.
```sh
# Redux + Plain JS template
npx create-react-app my-app --template redux
# Redux + TypeScript template
npx create-react-app my-app --template redux-typescript
```
Redux Toolkit includes these APIs:
- [`configureStore()`][cfg_store]: wraps `createStore` to provide simplified configuration options and good defaults.
It can automatically combines slice reducers, adds whatever Redux middleware supplied, includes redux-thunk by default, and enables use of the Redux DevTools Extension.
- [`createReducer()`][new_reducer]: that lets you supply a lookup table of action types to case reducer functions, rather than writing switch statements.
In addition, it automatically uses the `immer` library to let you write simpler immutable updates with normal mutative code, like `state.todos[3].completed = true`.
- [`createAction()`][new_action]: generates an action creator function for the given action type string.
The function itself has `toString()` defined, so that it can be used in place of the type constant.
- [`createSlice()`][new_slice]: accepts an object of reducer functions, a slice name, and an initial state value, and automatically generates a slice reducer with corresponding action creators and action types.
- [`createAsyncThunk`][new_async_thunk]: accepts an action type string and a function that returns a promise, and generates a thunk that dispatches pending/fulfilled/rejected action types based on that promise
- [`createEntityAdapter`][entity_adapt]: generates a set of reusable reducers and selectors to manage normalized data in the store
- The `createSelector` utility from the Reselect library, re-exported for ease of use.
[cfg_store]: https://redux-toolkit.js.org/api/configureStore
[new_reducer]: https://redux-toolkit.js.org/api/createReducer
[new_action]: https://redux-toolkit.js.org/api/createAction
[new_slice]: https://redux-toolkit.js.org/api/createSlice
[new_async_thunk]: https://redux-toolkit.js.org/api/createAsyncThunk
[entity_adapt]: https://redux-toolkit.js.org/api/createEntityAdapter
### [`configureStore`](https://redux-toolkit.js.org/api/configureStore)
Included Default Middleware:
- Immutability check middleware: deeply compares state values for mutations. It can detect mutations in reducers during a dispatch, and also mutations that occur between dispatches.
When a mutation is detected, it will throw an error and indicate the key path for where the mutated value was detected in the state tree. (Forked from `redux-immutable-state-invariant`.)
- Serializability check middleware: a custom middleware created specifically for use in Redux Toolkit
Similar in concept to `immutable-state-invariant`, but deeply checks the state tree and the actions for non-serializable values such as functions, Promises, Symbols, and other non-plain-JS-data values
When a non-serializable value is detected, a console error will be printed with the key path for where the non-serializable value was detected.
- In addition to these development tool middleware, it also adds `redux-thunk` by default, since thunks are the basic recommended side effects middleware for Redux.
Currently, the return value of `getDefaultMiddleware()` is:
```js
// development
const middleware = [thunk, immutableStateInvariant, serializableStateInvariant]
// production
const middleware = [thunk]
```
```js
import { combineReducers } from 'redux'
import { configureStore } from '@reduxjs/toolkit'
import monitorReducersEnhancer from './enhancers/monitorReducers'
import loggerMiddleware from './middleware/logger'
import usersReducer from './usersReducer'
import postsReducer from './postsReducer'
const rootReducer = combineReducers({
users: usersReducer,
posts: postsReducer,
})
const store = configureStore({
// reducers combined automatically
reducer: rootReducer,
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(loggerMiddleware),
enhancers: [monitorReducersEnhancer]
})
export default store
```
### [`createAction`](https://redux-toolkit.js.org/api/createAction)
```js
import { createAction } from '@reduxjs/toolkit';
const increment = createAction<number | undefined>('counter/increment');
const action = increment(); // { type: 'counter/increment' }
const action = increment(3); // { type: 'counter/increment', payload: 3 }
increment.toString(); // 'counter/increment'
```
### [`createReducer`](https://redux-toolkit.js.org/api/createReducer)
```js
import { createAction, createReducer } from '@reduxjs/toolkit'
interface CounterState {
value: number
}
const increment = createAction('counter/increment')
const decrement = createAction('counter/decrement')
const incrementByAmount = createAction<number>('counter/incrementByAmount')
const initialState = { value: 0 } as CounterState
const counterReducer = createReducer(initialState, (builder) => {
builder
.addCase(increment, (state, action) => {
state.value++
})
.addCase(decrement, (state, action) => {
state.value--
})
.addCase(incrementByAmount, (state, action) => {
state.value += action.payload
})
})
```
### [`createSlice`](https://redux-toolkit.js.org/api/createSlice)
A function that accepts an initial state, an object of reducer functions, and a "slice name", and automatically generates action creators and action types that correspond to the reducers and state.
Internally, it uses `createAction` and `createReducer`, so it's possible to use Immer to write "mutating" immutable updates.
**Note**: action types will have the `<slice-name>/<reducer-name>` shape.
```js
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
interface CounterState {
value: number
}
const initialState = { value: 0 } as CounterState
const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment(state) {
state.value++
},
decrement(state) {
state.value--
},
incrementByAmount(state, action: PayloadAction<number>) {
state.value += action.payload
},
},
})
export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducer
```
### [`createAsyncThunk`](https://redux-toolkit.js.org/api/createAsyncThunk)
The function `createAsyncThunk` returns a standard Redux thunk action creator.
The thunk action creator function will have plain action creators for the pending, fulfilled, and rejected cases attached as nested fields.
The `payloadCreator` function will be called with two arguments:
- `arg`: a single value, containing the first parameter that was passed to the thunk action creator when it was dispatched.
- `thunkAPI`: an object containing all of the parameters that are normally passed to a Redux thunk function, as well as additional options:
- `dispatch`: the Redux store dispatch method
- `getState`: the Redux store getState method
- `extra`: the "extra argument" given to the thunk middleware on setup, if available
- `requestId`: a unique string ID value that was automatically generated to identify this request sequence
- `signal`: an `AbortController.signal` object that may be used to see if another part of the app logic has marked this request as needing cancellation.
- [...]
The logic in the `payloadCreator` function may use any of these values as needed to calculate the result.
```js
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
const payloadCreator = async (arg, ThunkAPI): Promise<T> => { /* ... */ };
const thunk = createAsyncThunk("<action-type>", payloadCreator);
thunk.pending; // action creator that dispatches an '<action-type>/pending'
thunk.fulfilled; // action creator that dispatches an '<action-type>/fulfilled'
thunk.rejected; // action creator that dispatches an '<action-type>/rejected'
const slice = createSlice({
name: '<action-name>',
initialState,
reducers: { /* standard reducer logic, with auto-generated action types per reducer */ },
extraReducers: (builder) => {
// Add reducers for additional action types here, and handle loading state as needed
builder.addCase(thunk.fulfilled, (state, action) => { /* body of the reducer */ })
},
})
```
## RTK Query
RTK Query is provided as an optional addon within the `@reduxjs/toolkit` package.
It is purpose-built to solve the use case of data fetching and caching, supplying a compact, but powerful toolset to define an API interface layer got the app.
It is intended to simplify common cases for loading data in a web application, eliminating the need to hand-write data fetching & caching logic yourself.
RTK Query is included within the installation of the core Redux Toolkit package. It is available via either of the two entry points below:
```cs
import { createApi } from '@reduxjs/toolkit/query'
/* React-specific entry point that automatically generates hooks corresponding to the defined endpoints */
import { createApi } from '@reduxjs/toolkit/query/react'
```
RTK Query includes these APIs:
- [`createApi()`][new_api]: The core of RTK Query's functionality. It allows to define a set of endpoints describe how to retrieve data from a series of endpoints,
including configuration of how to fetch and transform that data.
- [`fetchBaseQuery()`][fetch_query]: A small wrapper around fetch that aims to simplify requests. Intended as the recommended baseQuery to be used in createApi for the majority of users.
- [`<ApiProvider />`][api_provider]: Can be used as a Provider if you do not already have a Redux store.
- [`setupListeners()`][setup_listener]: A utility used to enable refetchOnMount and refetchOnReconnect behaviors.
[new_api]: https://redux-toolkit.js.org/rtk-query/api/createApi
[fetch_query]: https://redux-toolkit.js.org/rtk-query/api/fetchBaseQuery
[api_provider]: https://redux-toolkit.js.org/rtk-query/api/ApiProvider
[setup_listener]: https://redux-toolkit.js.org/rtk-query/api/setupListeners