diff --git a/.eslintrc.cjs b/.eslintrc.cjs index d7cd5d68ffb..0f388f5c201 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -34,7 +34,7 @@ module.exports = { ], '@typescript-eslint/consistent-type-imports': [ 'error', - { prefer: 'type-imports', disallowTypeAnnotations: false }, - ], + { prefer: 'type-imports', disallowTypeAnnotations: false } + ] } } diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 8cffa4e1cf7..28219ad1f49 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,5 @@ blank_issues_enabled: false contact_links: - - name: 🤔 Questions and Help - url: https://redux.js.org/introduction/getting-started#help-and-discussion - about: This is a bug tracker, not a support system. For usage questions, please use our support resources. + - name: 🤔 Questions and Help + url: https://redux.js.org/introduction/getting-started#help-and-discussion + about: This is a bug tracker, not a support system. For usage questions, please use our support resources. diff --git a/README.md b/README.md index c2cf0d3f4ba..719c592b9e2 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Redux LogoRedux +# Redux LogoRedux Redux is a predictable state container for JavaScript apps. diff --git a/docs/api/combineReducers.md b/docs/api/combineReducers.md index a015f4fae29..76668298909 100644 --- a/docs/api/combineReducers.md +++ b/docs/api/combineReducers.md @@ -130,7 +130,7 @@ export default combineReducers({ #### `App.js` ```js -import { configureStore } from '@reduxjs/toolkit' +import { configureStore } from '@reduxjs/toolkit' import reducer from './reducers/index' const store = configureStore({ diff --git a/docs/usage/WritingTests.mdx b/docs/usage/WritingTests.mdx index 7c4ca003a72..d08b0a70a11 100644 --- a/docs/usage/WritingTests.mdx +++ b/docs/usage/WritingTests.mdx @@ -351,7 +351,7 @@ interface ExtendedRenderOptions extends Omit { export function renderWithProviders( ui: React.ReactElement, - extendedRenderOptions: ExtendedRenderOptions = {}, + extendedRenderOptions: ExtendedRenderOptions = {} ) { const { preloadedState = {}, @@ -367,7 +367,7 @@ export function renderWithProviders( // Return an object with the store and all of RTL's query functions return { store, - ...render(ui, { wrapper: Wrapper, ...renderOptions }), + ...render(ui, { wrapper: Wrapper, ...renderOptions }) } } ``` diff --git a/docs/usage/migrating-to-modern-redux.mdx b/docs/usage/migrating-to-modern-redux.mdx index 1f3e641e6e7..f87c8b807ac 100644 --- a/docs/usage/migrating-to-modern-redux.mdx +++ b/docs/usage/migrating-to-modern-redux.mdx @@ -130,7 +130,7 @@ This example shows several possible common tasks when setting up a Redux store: None of these are _required_, but they do show up frequently in real-world codebases. ```js title="Custom Store Setup: src/app/store.js" -import { configureStore, combineReducers} from '@reduxjs/toolkit' +import { configureStore, combineReducers } from '@reduxjs/toolkit' import { persistStore, persistReducer, @@ -139,7 +139,7 @@ import { PAUSE, PERSIST, PURGE, - REGISTER, + REGISTER } from 'redux-persist' import storage from 'redux-persist/lib/storage' import { PersistGate } from 'redux-persist/integration/react' @@ -163,7 +163,7 @@ const rootReducer = combineReducers({ const persistConfig = { key: 'root', version: 1, - storage, + storage } const persistedReducer = persistReducer(persistConfig, rootReducer) @@ -175,12 +175,12 @@ const store = configureStore({ const middleware = getDefaultMiddleware({ // Pass in a custom `extra` argument to the thunk middleware thunk: { - extraArgument: {serviceLayer} + extraArgument: { serviceLayer } }, // Customize the built-in serializability dev check serializableCheck: { - ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], - }, + ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER] + } }).concat(customMiddleware, api.middleware) // Conditionally add another middleware in dev @@ -191,9 +191,12 @@ const store = configureStore({ return middleware }, // Turn off devtools in prod, or pass options in dev - devTools: process.env.NODE_ENV === 'production' ? false : { - stateSanitizer: stateSanitizerForDevtools - } + devTools: + process.env.NODE_ENV === 'production' + ? false + : { + stateSanitizer: stateSanitizerForDevtools + } }) ``` @@ -817,7 +820,7 @@ export const store = createStore(rootReducer) export type RootAction = TodoActions | CounterActions // ❌ Common pattern: manually defining the root state type with each field export interface RootState { - todos: TodosState; + todos: TodosState counter: CounterState } diff --git a/errors.json b/errors.json index ac6da06ac50..3f0c8ad59e6 100644 --- a/errors.json +++ b/errors.json @@ -17,4 +17,4 @@ "15": "Dispatching while constructing your middleware is not allowed. Other middleware would not be applied to this dispatch.", "16": "bindActionCreators expected an object or a function, but instead received: ''. Did you write \"import ActionCreators from\" instead of \"import * as ActionCreators from\"?", "17": "Action \"type\" property must be a string. Instead, the actual type was: ''. Value was: '' (stringified)" -} \ No newline at end of file +} diff --git a/examples/async/public/index.html b/examples/async/public/index.html index 76356722a69..3ee866aa9a9 100644 --- a/examples/async/public/index.html +++ b/examples/async/public/index.html @@ -1,8 +1,8 @@ - + - - + + Redux Async Example diff --git a/examples/async/src/components/Picker.js b/examples/async/src/components/Picker.js index 5ee3250ce67..7cb6ce651af 100644 --- a/examples/async/src/components/Picker.js +++ b/examples/async/src/components/Picker.js @@ -4,21 +4,18 @@ import PropTypes from 'prop-types' const Picker = ({ value, onChange, options }) => (

{value}

- onChange(e.target.value)} value={value}> + {options.map(option => ( ) - } + + ))}
) Picker.propTypes = { - options: PropTypes.arrayOf( - PropTypes.string.isRequired - ).isRequired, + options: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired, value: PropTypes.string.isRequired, onChange: PropTypes.func.isRequired } diff --git a/examples/async/src/components/Posts.js b/examples/async/src/components/Posts.js index 88f6cf263b4..21ecab636d3 100644 --- a/examples/async/src/components/Posts.js +++ b/examples/async/src/components/Posts.js @@ -1,11 +1,11 @@ import React from 'react' import PropTypes from 'prop-types' -const Posts = ({posts}) => ( +const Posts = ({ posts }) => ( ) diff --git a/examples/async/src/containers/App.js b/examples/async/src/containers/App.js index a3006941905..710de7334a5 100644 --- a/examples/async/src/containers/App.js +++ b/examples/async/src/containers/App.js @@ -1,7 +1,11 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import { connect } from 'react-redux' -import { selectSubreddit, fetchPostsIfNeeded, invalidateSubreddit } from '../actions' +import { + selectSubreddit, + fetchPostsIfNeeded, + invalidateSubreddit +} from '../actions' import Picker from '../components/Picker' import Posts from '../components/Posts' @@ -43,28 +47,32 @@ class App extends Component { const isEmpty = posts.length === 0 return (
- +

- {lastUpdated && + {lastUpdated && ( - Last updated at {new Date(lastUpdated).toLocaleTimeString()}. - {' '} + Last updated at {new Date(lastUpdated).toLocaleTimeString()}.{' '} - } - {!isFetching && - - } + )} + {!isFetching && ( + + )}

- {isEmpty - ? (isFetching ?

Loading...

:

Empty.

) - :
- -
- } + {isEmpty ? ( + isFetching ? ( +

Loading...

+ ) : ( +

Empty.

+ ) + ) : ( +
+ +
+ )}
) } diff --git a/examples/async/src/index.js b/examples/async/src/index.js index 32b33a6b608..7bd47af1229 100644 --- a/examples/async/src/index.js +++ b/examples/async/src/index.js @@ -7,15 +7,12 @@ import { createLogger } from 'redux-logger' import reducer from './reducers' import App from './containers/App' -const middleware = [ thunk ] +const middleware = [thunk] if (process.env.NODE_ENV !== 'production') { middleware.push(createLogger()) } -const store = createStore( - reducer, - applyMiddleware(...middleware) -) +const store = createStore(reducer, applyMiddleware(...middleware)) render( diff --git a/examples/async/src/reducers/index.js b/examples/async/src/reducers/index.js index 7f58bcd559a..c63a3d6f12d 100644 --- a/examples/async/src/reducers/index.js +++ b/examples/async/src/reducers/index.js @@ -1,7 +1,9 @@ import { combineReducers } from 'redux' import { - SELECT_SUBREDDIT, INVALIDATE_SUBREDDIT, - REQUEST_POSTS, RECEIVE_POSTS + SELECT_SUBREDDIT, + INVALIDATE_SUBREDDIT, + REQUEST_POSTS, + RECEIVE_POSTS } from '../actions' const selectedSubreddit = (state = 'reactjs', action) => { @@ -13,11 +15,14 @@ const selectedSubreddit = (state = 'reactjs', action) => { } } -const posts = (state = { - isFetching: false, - didInvalidate: false, - items: [] -}, action) => { +const posts = ( + state = { + isFetching: false, + didInvalidate: false, + items: [] + }, + action +) => { switch (action.type) { case INVALIDATE_SUBREDDIT: return { @@ -43,7 +48,7 @@ const posts = (state = { } } -const postsBySubreddit = (state = { }, action) => { +const postsBySubreddit = (state = {}, action) => { switch (action.type) { case INVALIDATE_SUBREDDIT: case RECEIVE_POSTS: diff --git a/examples/counter-ts/src/App.test.tsx b/examples/counter-ts/src/App.test.tsx index 659cc13d3bb..33e2b79bd87 100644 --- a/examples/counter-ts/src/App.test.tsx +++ b/examples/counter-ts/src/App.test.tsx @@ -1,15 +1,15 @@ -import React from 'react'; -import { render } from '@testing-library/react'; -import { Provider } from 'react-redux'; -import { store } from './app/store'; -import App from './App'; +import React from 'react' +import { render } from '@testing-library/react' +import { Provider } from 'react-redux' +import { store } from './app/store' +import App from './App' test('renders learn react link', () => { const { getByText } = render( - ); + ) - expect(getByText(/learn/i)).toBeInTheDocument(); -}); + expect(getByText(/learn/i)).toBeInTheDocument() +}) diff --git a/examples/counter-ts/src/app/hooks.ts b/examples/counter-ts/src/app/hooks.ts index b0d4821e0b9..02834535077 100644 --- a/examples/counter-ts/src/app/hooks.ts +++ b/examples/counter-ts/src/app/hooks.ts @@ -3,4 +3,4 @@ import type { AppDispatch, RootState } from './store' // Use throughout your app instead of plain `useDispatch` and `useSelector` export const useAppDispatch = useDispatch.withTypes() -export const useAppSelector = useSelector.withTypes() \ No newline at end of file +export const useAppSelector = useSelector.withTypes() diff --git a/examples/counter-ts/src/app/store.ts b/examples/counter-ts/src/app/store.ts index 133ff34eec1..8c4118ebd9f 100644 --- a/examples/counter-ts/src/app/store.ts +++ b/examples/counter-ts/src/app/store.ts @@ -1,17 +1,17 @@ -import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit'; -import counterReducer from '../features/counter/counterSlice'; +import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit' +import counterReducer from '../features/counter/counterSlice' export const store = configureStore({ reducer: { - counter: counterReducer, - }, -}); + counter: counterReducer + } +}) -export type AppDispatch = typeof store.dispatch; -export type RootState = ReturnType; +export type AppDispatch = typeof store.dispatch +export type RootState = ReturnType export type AppThunk = ThunkAction< ReturnType, RootState, unknown, Action ->; +> diff --git a/examples/counter-ts/src/features/counter/Counter.tsx b/examples/counter-ts/src/features/counter/Counter.tsx index ece5191bae3..222d6074c4c 100644 --- a/examples/counter-ts/src/features/counter/Counter.tsx +++ b/examples/counter-ts/src/features/counter/Counter.tsx @@ -1,22 +1,22 @@ -import React, { useState } from 'react'; +import React, { useState } from 'react' -import { useAppSelector, useAppDispatch } from '../../app/hooks'; +import { useAppSelector, useAppDispatch } from '../../app/hooks' import { decrement, increment, incrementByAmount, incrementAsync, incrementIfOdd, - selectCount, -} from './counterSlice'; -import styles from './Counter.module.css'; + selectCount +} from './counterSlice' +import styles from './Counter.module.css' export function Counter() { - const count = useAppSelector(selectCount); - const dispatch = useAppDispatch(); - const [incrementAmount, setIncrementAmount] = useState('2'); + const count = useAppSelector(selectCount) + const dispatch = useAppDispatch() + const [incrementAmount, setIncrementAmount] = useState('2') - const incrementValue = Number(incrementAmount) || 0; + const incrementValue = Number(incrementAmount) || 0 return (
@@ -42,7 +42,7 @@ export function Counter() { className={styles.textbox} aria-label="Set increment amount" value={incrementAmount} - onChange={(e) => setIncrementAmount(e.target.value)} + onChange={e => setIncrementAmount(e.target.value)} />
- ); + ) } diff --git a/examples/counter-ts/src/features/counter/counterAPI.ts b/examples/counter-ts/src/features/counter/counterAPI.ts index 0a9cdd31b9b..f0dcc12f1da 100644 --- a/examples/counter-ts/src/features/counter/counterAPI.ts +++ b/examples/counter-ts/src/features/counter/counterAPI.ts @@ -1,6 +1,6 @@ // A mock function to mimic making an async request for data export function fetchCount(amount = 1) { - return new Promise<{ data: number }>((resolve) => + return new Promise<{ data: number }>(resolve => setTimeout(() => resolve({ data: amount }), 500) - ); + ) } diff --git a/examples/counter-ts/src/features/counter/counterSlice.spec.ts b/examples/counter-ts/src/features/counter/counterSlice.spec.ts index 098163b9ad9..82049d5c45b 100644 --- a/examples/counter-ts/src/features/counter/counterSlice.spec.ts +++ b/examples/counter-ts/src/features/counter/counterSlice.spec.ts @@ -2,33 +2,33 @@ import counterReducer, { CounterState, increment, decrement, - incrementByAmount, -} from './counterSlice'; + incrementByAmount +} from './counterSlice' describe('counter reducer', () => { const initialState: CounterState = { value: 3, - status: 'idle', - }; + status: 'idle' + } it('should handle initial state', () => { expect(counterReducer(undefined, { type: 'unknown' })).toEqual({ value: 0, - status: 'idle', - }); - }); + status: 'idle' + }) + }) it('should handle increment', () => { - const actual = counterReducer(initialState, increment()); - expect(actual.value).toEqual(4); - }); + const actual = counterReducer(initialState, increment()) + expect(actual.value).toEqual(4) + }) it('should handle decrement', () => { - const actual = counterReducer(initialState, decrement()); - expect(actual.value).toEqual(2); - }); + const actual = counterReducer(initialState, decrement()) + expect(actual.value).toEqual(2) + }) it('should handle incrementByAmount', () => { - const actual = counterReducer(initialState, incrementByAmount(2)); - expect(actual.value).toEqual(5); - }); -}); + const actual = counterReducer(initialState, incrementByAmount(2)) + expect(actual.value).toEqual(5) + }) +}) diff --git a/examples/counter-ts/src/features/counter/counterSlice.ts b/examples/counter-ts/src/features/counter/counterSlice.ts index 015dca0aaea..0c30ed857e1 100644 --- a/examples/counter-ts/src/features/counter/counterSlice.ts +++ b/examples/counter-ts/src/features/counter/counterSlice.ts @@ -1,16 +1,16 @@ -import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'; -import { RootState, AppThunk } from '../../app/store'; -import { fetchCount } from './counterAPI'; +import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit' +import { RootState, AppThunk } from '../../app/store' +import { fetchCount } from './counterAPI' export interface CounterState { - value: number; - status: 'idle' | 'loading' | 'failed'; + value: number + status: 'idle' | 'loading' | 'failed' } const initialState: CounterState = { value: 0, - status: 'idle', -}; + status: 'idle' +} // The function below is called a thunk and allows us to perform async logic. It // can be dispatched like a regular action: `dispatch(incrementAsync(10))`. This @@ -20,65 +20,65 @@ const initialState: CounterState = { export const incrementAsync = createAsyncThunk( 'counter/fetchCount', async (amount: number) => { - const response = await fetchCount(amount); + const response = await fetchCount(amount) // The value we return becomes the `fulfilled` action payload - return response.data; + return response.data } -); +) export const counterSlice = createSlice({ name: 'counter', initialState, // The `reducers` field lets us define reducers and generate associated actions reducers: { - increment: (state) => { + increment: state => { // Redux Toolkit allows us to write "mutating" logic in reducers. It // doesn't actually mutate the state because it uses the Immer library, // which detects changes to a "draft state" and produces a brand new // immutable state based off those changes - state.value += 1; + state.value += 1 }, - decrement: (state) => { - state.value -= 1; + decrement: state => { + state.value -= 1 }, // Use the PayloadAction type to declare the contents of `action.payload` incrementByAmount: (state, action: PayloadAction) => { - state.value += action.payload; - }, + state.value += action.payload + } }, // The `extraReducers` field lets the slice handle actions defined elsewhere, // including actions generated by createAsyncThunk or in other slices. - extraReducers: (builder) => { + extraReducers: builder => { builder - .addCase(incrementAsync.pending, (state) => { - state.status = 'loading'; + .addCase(incrementAsync.pending, state => { + state.status = 'loading' }) .addCase(incrementAsync.fulfilled, (state, action) => { - state.status = 'idle'; - state.value += action.payload; + state.status = 'idle' + state.value += action.payload }) - .addCase(incrementAsync.rejected, (state) => { - state.status = 'failed'; - }); - }, -}); + .addCase(incrementAsync.rejected, state => { + state.status = 'failed' + }) + } +}) -export const { increment, decrement, incrementByAmount } = counterSlice.actions; +export const { increment, decrement, incrementByAmount } = counterSlice.actions // The function below is called a selector and allows us to select a value from // the state. Selectors can also be defined inline where they're used instead of // in the slice file. For example: `useSelector((state: RootState) => state.counter.value)` -export const selectCount = (state: RootState) => state.counter.value; +export const selectCount = (state: RootState) => state.counter.value // We can also write thunks by hand, which may contain both sync and async logic. // Here's an example of conditionally dispatching actions based on current state. export const incrementIfOdd = (amount: number): AppThunk => (dispatch, getState) => { - const currentValue = selectCount(getState()); + const currentValue = selectCount(getState()) if (currentValue % 2 === 1) { - dispatch(incrementByAmount(amount)); + dispatch(incrementByAmount(amount)) } - }; + } -export default counterSlice.reducer; +export default counterSlice.reducer diff --git a/examples/counter-ts/src/index.tsx b/examples/counter-ts/src/index.tsx index 0e6466fa15c..c920ec95bc1 100644 --- a/examples/counter-ts/src/index.tsx +++ b/examples/counter-ts/src/index.tsx @@ -1,13 +1,13 @@ -import React from 'react'; -import { createRoot } from 'react-dom/client'; -import { Provider } from 'react-redux'; -import { store } from './app/store'; -import App from './App'; -import reportWebVitals from './reportWebVitals'; -import './index.css'; +import React from 'react' +import { createRoot } from 'react-dom/client' +import { Provider } from 'react-redux' +import { store } from './app/store' +import App from './App' +import reportWebVitals from './reportWebVitals' +import './index.css' -const container = document.getElementById('root')!; -const root = createRoot(container); +const container = document.getElementById('root')! +const root = createRoot(container) root.render( @@ -15,9 +15,9 @@ root.render(
-); +) // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); +reportWebVitals() diff --git a/examples/counter-ts/src/reportWebVitals.ts b/examples/counter-ts/src/reportWebVitals.ts index 49a2a16e0fb..57a24a21eae 100644 --- a/examples/counter-ts/src/reportWebVitals.ts +++ b/examples/counter-ts/src/reportWebVitals.ts @@ -1,15 +1,15 @@ -import { ReportHandler } from 'web-vitals'; +import { ReportHandler } from 'web-vitals' const reportWebVitals = (onPerfEntry?: ReportHandler) => { if (onPerfEntry && onPerfEntry instanceof Function) { import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - getCLS(onPerfEntry); - getFID(onPerfEntry); - getFCP(onPerfEntry); - getLCP(onPerfEntry); - getTTFB(onPerfEntry); - }); + getCLS(onPerfEntry) + getFID(onPerfEntry) + getFCP(onPerfEntry) + getLCP(onPerfEntry) + getTTFB(onPerfEntry) + }) } -}; +} -export default reportWebVitals; +export default reportWebVitals diff --git a/examples/counter-ts/tsconfig.json b/examples/counter-ts/tsconfig.json index a273b0cfc0e..9d379a3c4af 100644 --- a/examples/counter-ts/tsconfig.json +++ b/examples/counter-ts/tsconfig.json @@ -1,11 +1,7 @@ { "compilerOptions": { "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], + "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, @@ -20,7 +16,5 @@ "noEmit": true, "jsx": "react-jsx" }, - "include": [ - "src" - ] + "include": ["src"] } diff --git a/examples/counter-vanilla/index.html b/examples/counter-vanilla/index.html index 61409c88dc8..f5d510fd5f9 100644 --- a/examples/counter-vanilla/index.html +++ b/examples/counter-vanilla/index.html @@ -41,24 +41,28 @@ render() store.subscribe(render) - document.getElementById('increment') + document + .getElementById('increment') .addEventListener('click', function () { store.dispatch({ type: 'INCREMENT' }) }) - document.getElementById('decrement') + document + .getElementById('decrement') .addEventListener('click', function () { store.dispatch({ type: 'DECREMENT' }) }) - document.getElementById('incrementIfOdd') + document + .getElementById('incrementIfOdd') .addEventListener('click', function () { if (store.getState() % 2 !== 0) { store.dispatch({ type: 'INCREMENT' }) } }) - document.getElementById('incrementAsync') + document + .getElementById('incrementAsync') .addEventListener('click', function () { setTimeout(function () { store.dispatch({ type: 'INCREMENT' }) diff --git a/examples/counter/src/App.test.js b/examples/counter/src/App.test.js index 980638bdf3b..527874cee80 100644 --- a/examples/counter/src/App.test.js +++ b/examples/counter/src/App.test.js @@ -1,15 +1,15 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import { Provider } from 'react-redux'; -import { store } from './app/store'; -import App from './App'; +import React from 'react' +import { render, screen } from '@testing-library/react' +import { Provider } from 'react-redux' +import { store } from './app/store' +import App from './App' test('renders learn react link', () => { render( - ); + ) - expect(screen.getByText(/learn/i)).toBeInTheDocument(); -}); + expect(screen.getByText(/learn/i)).toBeInTheDocument() +}) diff --git a/examples/counter/src/app/store.js b/examples/counter/src/app/store.js index 9eca6d24b1e..b7eca242d53 100644 --- a/examples/counter/src/app/store.js +++ b/examples/counter/src/app/store.js @@ -1,8 +1,8 @@ -import { configureStore } from '@reduxjs/toolkit'; -import counterReducer from '../features/counter/counterSlice'; +import { configureStore } from '@reduxjs/toolkit' +import counterReducer from '../features/counter/counterSlice' export const store = configureStore({ reducer: { - counter: counterReducer, - }, -}); + counter: counterReducer + } +}) diff --git a/examples/counter/src/features/counter/Counter.js b/examples/counter/src/features/counter/Counter.js index 772a6bafeff..122d4cffb0e 100644 --- a/examples/counter/src/features/counter/Counter.js +++ b/examples/counter/src/features/counter/Counter.js @@ -1,21 +1,21 @@ -import React, { useState } from 'react'; -import { useSelector, useDispatch } from 'react-redux'; +import React, { useState } from 'react' +import { useSelector, useDispatch } from 'react-redux' import { decrement, increment, incrementByAmount, incrementAsync, incrementIfOdd, - selectCount, -} from './counterSlice'; -import styles from './Counter.module.css'; + selectCount +} from './counterSlice' +import styles from './Counter.module.css' export function Counter() { - const count = useSelector(selectCount); - const dispatch = useDispatch(); - const [incrementAmount, setIncrementAmount] = useState('2'); + const count = useSelector(selectCount) + const dispatch = useDispatch() + const [incrementAmount, setIncrementAmount] = useState('2') - const incrementValue = Number(incrementAmount) || 0; + const incrementValue = Number(incrementAmount) || 0 return (
@@ -41,7 +41,7 @@ export function Counter() { className={styles.textbox} aria-label="Set increment amount" value={incrementAmount} - onChange={(e) => setIncrementAmount(e.target.value)} + onChange={e => setIncrementAmount(e.target.value)} />
- ); + ) } diff --git a/examples/counter/src/features/counter/counterAPI.js b/examples/counter/src/features/counter/counterAPI.js index cc9b4a44238..da8f77cf424 100644 --- a/examples/counter/src/features/counter/counterAPI.js +++ b/examples/counter/src/features/counter/counterAPI.js @@ -1,6 +1,6 @@ // A mock function to mimic making an async request for data export function fetchCount(amount = 1) { - return new Promise((resolve) => + return new Promise(resolve => setTimeout(() => resolve({ data: amount }), 500) - ); + ) } diff --git a/examples/counter/src/features/counter/counterSlice.js b/examples/counter/src/features/counter/counterSlice.js index 8dc4b5cff7b..35c666c8046 100644 --- a/examples/counter/src/features/counter/counterSlice.js +++ b/examples/counter/src/features/counter/counterSlice.js @@ -1,10 +1,10 @@ -import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; -import { fetchCount } from './counterAPI'; +import { createAsyncThunk, createSlice } from '@reduxjs/toolkit' +import { fetchCount } from './counterAPI' const initialState = { value: 0, - status: 'idle', -}; + status: 'idle' +} // The function below is called a thunk and allows us to perform async logic. It // can be dispatched like a regular action: `dispatch(incrementAsync(10))`. This @@ -13,61 +13,61 @@ const initialState = { // typically used to make async requests. export const incrementAsync = createAsyncThunk( 'counter/fetchCount', - async (amount) => { - const response = await fetchCount(amount); + async amount => { + const response = await fetchCount(amount) // The value we return becomes the `fulfilled` action payload - return response.data; + return response.data } -); +) export const counterSlice = createSlice({ name: 'counter', initialState, // The `reducers` field lets us define reducers and generate associated actions reducers: { - increment: (state) => { + increment: state => { // Redux Toolkit allows us to write "mutating" logic in reducers. It // doesn't actually mutate the state because it uses the Immer library, // which detects changes to a "draft state" and produces a brand new // immutable state based off those changes - state.value += 1; + state.value += 1 }, - decrement: (state) => { - state.value -= 1; + decrement: state => { + state.value -= 1 }, // Use the PayloadAction type to declare the contents of `action.payload` incrementByAmount: (state, action) => { - state.value += action.payload; - }, + state.value += action.payload + } }, // The `extraReducers` field lets the slice handle actions defined elsewhere, // including actions generated by createAsyncThunk or in other slices. - extraReducers: (builder) => { + extraReducers: builder => { builder - .addCase(incrementAsync.pending, (state) => { - state.status = 'loading'; + .addCase(incrementAsync.pending, state => { + state.status = 'loading' }) .addCase(incrementAsync.fulfilled, (state, action) => { - state.status = 'idle'; - state.value += action.payload; - }); - }, -}); + state.status = 'idle' + state.value += action.payload + }) + } +}) -export const { increment, decrement, incrementByAmount } = counterSlice.actions; +export const { increment, decrement, incrementByAmount } = counterSlice.actions // The function below is called a selector and allows us to select a value from // the state. Selectors can also be defined inline where they're used instead of // in the slice file. For example: `useSelector((state: RootState) => state.counter.value)` -export const selectCount = (state) => state.counter.value; +export const selectCount = state => state.counter.value // We can also write thunks by hand, which may contain both sync and async logic. // Here's an example of conditionally dispatching actions based on current state. -export const incrementIfOdd = (amount) => (dispatch, getState) => { - const currentValue = selectCount(getState()); +export const incrementIfOdd = amount => (dispatch, getState) => { + const currentValue = selectCount(getState()) if (currentValue % 2 === 1) { - dispatch(incrementByAmount(amount)); + dispatch(incrementByAmount(amount)) } -}; +} -export default counterSlice.reducer; +export default counterSlice.reducer diff --git a/examples/counter/src/features/counter/counterSlice.spec.js b/examples/counter/src/features/counter/counterSlice.spec.js index c1fed2ce59e..8e7b34416c8 100644 --- a/examples/counter/src/features/counter/counterSlice.spec.js +++ b/examples/counter/src/features/counter/counterSlice.spec.js @@ -1,33 +1,33 @@ import counterReducer, { increment, decrement, - incrementByAmount, -} from './counterSlice'; + incrementByAmount +} from './counterSlice' describe('counter reducer', () => { const initialState = { value: 3, - status: 'idle', - }; + status: 'idle' + } it('should handle initial state', () => { expect(counterReducer(undefined, { type: 'unknown' })).toEqual({ value: 0, - status: 'idle', - }); - }); + status: 'idle' + }) + }) it('should handle increment', () => { - const actual = counterReducer(initialState, increment()); - expect(actual.value).toEqual(4); - }); + const actual = counterReducer(initialState, increment()) + expect(actual.value).toEqual(4) + }) it('should handle decrement', () => { - const actual = counterReducer(initialState, decrement()); - expect(actual.value).toEqual(2); - }); + const actual = counterReducer(initialState, decrement()) + expect(actual.value).toEqual(2) + }) it('should handle incrementByAmount', () => { - const actual = counterReducer(initialState, incrementByAmount(2)); - expect(actual.value).toEqual(5); - }); -}); + const actual = counterReducer(initialState, incrementByAmount(2)) + expect(actual.value).toEqual(5) + }) +}) diff --git a/examples/counter/src/index.js b/examples/counter/src/index.js index 732a8cc84af..25568a01f04 100644 --- a/examples/counter/src/index.js +++ b/examples/counter/src/index.js @@ -1,13 +1,13 @@ -import React from 'react'; -import { createRoot } from 'react-dom/client'; -import { Provider } from 'react-redux'; -import { store } from './app/store'; -import App from './App'; -import reportWebVitals from './reportWebVitals'; -import './index.css'; +import React from 'react' +import { createRoot } from 'react-dom/client' +import { Provider } from 'react-redux' +import { store } from './app/store' +import App from './App' +import reportWebVitals from './reportWebVitals' +import './index.css' -const container = document.getElementById('root'); -const root = createRoot(container); +const container = document.getElementById('root') +const root = createRoot(container) root.render( @@ -15,9 +15,9 @@ root.render( -); +) // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); +reportWebVitals() diff --git a/examples/counter/src/reportWebVitals.js b/examples/counter/src/reportWebVitals.js index 5253d3ad9e6..9381231d219 100644 --- a/examples/counter/src/reportWebVitals.js +++ b/examples/counter/src/reportWebVitals.js @@ -1,13 +1,13 @@ const reportWebVitals = onPerfEntry => { if (onPerfEntry && onPerfEntry instanceof Function) { import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - getCLS(onPerfEntry); - getFID(onPerfEntry); - getFCP(onPerfEntry); - getLCP(onPerfEntry); - getTTFB(onPerfEntry); - }); + getCLS(onPerfEntry) + getFID(onPerfEntry) + getFCP(onPerfEntry) + getLCP(onPerfEntry) + getTTFB(onPerfEntry) + }) } -}; +} -export default reportWebVitals; +export default reportWebVitals diff --git a/examples/real-world/public/index.html b/examples/real-world/public/index.html index 72e10e94c6c..75e9a53d7da 100644 --- a/examples/real-world/public/index.html +++ b/examples/real-world/public/index.html @@ -1,8 +1,8 @@ - + - - + + React App diff --git a/examples/real-world/src/actions/index.js b/examples/real-world/src/actions/index.js index b57ae627bd0..20bb3f4c99b 100644 --- a/examples/real-world/src/actions/index.js +++ b/examples/real-world/src/actions/index.js @@ -8,7 +8,7 @@ export const USER_FAILURE = 'USER_FAILURE' // Relies on the custom API middleware defined in ../middleware/api.js. const fetchUser = login => ({ [CALL_API]: { - types: [ USER_REQUEST, USER_SUCCESS, USER_FAILURE ], + types: [USER_REQUEST, USER_SUCCESS, USER_FAILURE], endpoint: `users/${login}`, schema: Schemas.USER } @@ -16,15 +16,17 @@ const fetchUser = login => ({ // Fetches a single user from Github API unless it is cached. // Relies on Redux Thunk middleware. -export const loadUser = (login, requiredFields = []) => (dispatch, getState) => { - const user = getState().entities.users[login] - if (user && requiredFields.every(key => user.hasOwnProperty(key))) { - return null +export const loadUser = + (login, requiredFields = []) => + (dispatch, getState) => { + const user = getState().entities.users[login] + if (user && requiredFields.every(key => user.hasOwnProperty(key))) { + return null + } + + return dispatch(fetchUser(login)) } - return dispatch(fetchUser(login)) -} - export const REPO_REQUEST = 'REPO_REQUEST' export const REPO_SUCCESS = 'REPO_SUCCESS' export const REPO_FAILURE = 'REPO_FAILURE' @@ -33,7 +35,7 @@ export const REPO_FAILURE = 'REPO_FAILURE' // Relies on the custom API middleware defined in ../middleware/api.js. const fetchRepo = fullName => ({ [CALL_API]: { - types: [ REPO_REQUEST, REPO_SUCCESS, REPO_FAILURE ], + types: [REPO_REQUEST, REPO_SUCCESS, REPO_FAILURE], endpoint: `repos/${fullName}`, schema: Schemas.REPO } @@ -41,15 +43,17 @@ const fetchRepo = fullName => ({ // Fetches a single repository from Github API unless it is cached. // Relies on Redux Thunk middleware. -export const loadRepo = (fullName, requiredFields = []) => (dispatch, getState) => { - const repo = getState().entities.repos[fullName] - if (repo && requiredFields.every(key => repo.hasOwnProperty(key))) { - return null +export const loadRepo = + (fullName, requiredFields = []) => + (dispatch, getState) => { + const repo = getState().entities.repos[fullName] + if (repo && requiredFields.every(key => repo.hasOwnProperty(key))) { + return null + } + + return dispatch(fetchRepo(fullName)) } - return dispatch(fetchRepo(fullName)) -} - export const STARRED_REQUEST = 'STARRED_REQUEST' export const STARRED_SUCCESS = 'STARRED_SUCCESS' export const STARRED_FAILURE = 'STARRED_FAILURE' @@ -59,7 +63,7 @@ export const STARRED_FAILURE = 'STARRED_FAILURE' const fetchStarred = (login, nextPageUrl) => ({ login, [CALL_API]: { - types: [ STARRED_REQUEST, STARRED_SUCCESS, STARRED_FAILURE ], + types: [STARRED_REQUEST, STARRED_SUCCESS, STARRED_FAILURE], endpoint: nextPageUrl, schema: Schemas.REPO_ARRAY } @@ -69,10 +73,8 @@ const fetchStarred = (login, nextPageUrl) => ({ // Bails out if page is cached and user didn't specifically request next page. // Relies on Redux Thunk middleware. export const loadStarred = (login, nextPage) => (dispatch, getState) => { - const { - nextPageUrl = `users/${login}/starred`, - pageCount = 0 - } = getState().pagination.starredByUser[login] || {} + const { nextPageUrl = `users/${login}/starred`, pageCount = 0 } = + getState().pagination.starredByUser[login] || {} if (pageCount > 0 && !nextPage) { return null @@ -90,7 +92,7 @@ export const STARGAZERS_FAILURE = 'STARGAZERS_FAILURE' const fetchStargazers = (fullName, nextPageUrl) => ({ fullName, [CALL_API]: { - types: [ STARGAZERS_REQUEST, STARGAZERS_SUCCESS, STARGAZERS_FAILURE ], + types: [STARGAZERS_REQUEST, STARGAZERS_SUCCESS, STARGAZERS_FAILURE], endpoint: nextPageUrl, schema: Schemas.USER_ARRAY } @@ -100,10 +102,8 @@ const fetchStargazers = (fullName, nextPageUrl) => ({ // Bails out if page is cached and user didn't specifically request next page. // Relies on Redux Thunk middleware. export const loadStargazers = (fullName, nextPage) => (dispatch, getState) => { - const { - nextPageUrl = `repos/${fullName}/stargazers`, - pageCount = 0 - } = getState().pagination.stargazersByRepo[fullName] || {} + const { nextPageUrl = `repos/${fullName}/stargazers`, pageCount = 0 } = + getState().pagination.stargazersByRepo[fullName] || {} if (pageCount > 0 && !nextPage) { return null @@ -116,5 +116,5 @@ export const RESET_ERROR_MESSAGE = 'RESET_ERROR_MESSAGE' // Resets the currently visible error message. export const resetErrorMessage = () => ({ - type: RESET_ERROR_MESSAGE + type: RESET_ERROR_MESSAGE }) diff --git a/examples/real-world/src/components/Explore.js b/examples/real-world/src/components/Explore.js index 56168bc5ee1..8f511f16600 100644 --- a/examples/real-world/src/components/Explore.js +++ b/examples/real-world/src/components/Explore.js @@ -21,14 +21,14 @@ export default class Explore extends Component { return this.input.value } - setInputValue = (val) => { + setInputValue = val => { // Generally mutating DOM is a bad idea in React components, // but doing this for a single uncontrolled field is less fuss // than making it controlled and maintaining a state for it. this.input.value = val } - handleKeyUp = (e) => { + handleKeyUp = e => { if (e.keyCode === 13) { this.handleGoClick() } @@ -42,19 +42,21 @@ export default class Explore extends Component { return (

Type a username or repo full name and hit 'Go':

- this.input = input} - defaultValue={this.props.value} - onKeyUp={this.handleKeyUp} /> - + (this.input = input)} + defaultValue={this.props.value} + onKeyUp={this.handleKeyUp} + /> +

- Code on Github. -

-

- Move the DevTools with Ctrl+W or hide them with Ctrl+H. + Code on{' '} + + Github + + .

+

Move the DevTools with Ctrl+W or hide them with Ctrl+H.

) } diff --git a/examples/real-world/src/components/List.js b/examples/real-world/src/components/List.js index 2e4e85ee0a0..e52f5557f6e 100644 --- a/examples/real-world/src/components/List.js +++ b/examples/real-world/src/components/List.js @@ -22,9 +22,11 @@ export default class List extends Component { renderLoadMore() { const { isFetching, onLoadMoreClick } = this.props return ( - ) @@ -32,18 +34,30 @@ export default class List extends Component { render() { const { - isFetching, nextPageUrl, pageCount, - items, renderItem, loadingLabel + isFetching, + nextPageUrl, + pageCount, + items, + renderItem, + loadingLabel } = this.props const isEmpty = items.length === 0 if (isEmpty && isFetching) { - return

{loadingLabel}

+ return ( +

+ {loadingLabel} +

+ ) } const isLastPage = !nextPageUrl if (isEmpty && isLastPage) { - return

Nothing here!

+ return ( +

+ Nothing here! +

+ ) } return ( diff --git a/examples/real-world/src/components/Repo.js b/examples/real-world/src/components/Repo.js index 1eb99c084ae..fcc3021868d 100644 --- a/examples/real-world/src/components/Repo.js +++ b/examples/real-world/src/components/Repo.js @@ -9,17 +9,11 @@ const Repo = ({ repo, owner }) => { return (

- - {name} - + {name} {' by '} - - {login} - + {login}

- {description && -

{description}

- } + {description &&

{description}

}
) } diff --git a/examples/real-world/src/containers/App.js b/examples/real-world/src/containers/App.js index f06c5e95836..3e609226668 100644 --- a/examples/real-world/src/containers/App.js +++ b/examples/real-world/src/containers/App.js @@ -34,11 +34,8 @@ class App extends Component { return (

- {errorMessage} - {' '} - + {errorMessage}{' '} +

) } @@ -47,8 +44,7 @@ class App extends Component { const { children, inputValue } = this.props return (
- +
{this.renderErrorMessage()} {children} @@ -62,6 +58,8 @@ const mapStateToProps = (state, ownProps) => ({ inputValue: ownProps.location.pathname.substring(1) }) -export default withRouter(connect(mapStateToProps, { - resetErrorMessage -})(App)) +export default withRouter( + connect(mapStateToProps, { + resetErrorMessage + })(App) +) diff --git a/examples/real-world/src/containers/DevTools.js b/examples/real-world/src/containers/DevTools.js index ad1b6fdc712..b079e71bd7e 100644 --- a/examples/real-world/src/containers/DevTools.js +++ b/examples/real-world/src/containers/DevTools.js @@ -4,8 +4,7 @@ import LogMonitor from 'redux-devtools-log-monitor' import DockMonitor from 'redux-devtools-dock-monitor' export default createDevTools( - + ) diff --git a/examples/real-world/src/containers/RepoPage.js b/examples/real-world/src/containers/RepoPage.js index 05a1b69c565..0a2530c1001 100644 --- a/examples/real-world/src/containers/RepoPage.js +++ b/examples/real-world/src/containers/RepoPage.js @@ -11,7 +11,7 @@ import List from '../components/List' const loadData = props => { const { fullName } = props - props.loadRepo(fullName, [ 'description' ]) + props.loadRepo(fullName, ['description']) props.loadStargazers(fullName) } @@ -48,20 +48,25 @@ class RepoPage extends Component { render() { const { repo, owner, name } = this.props if (!repo || !owner) { - return

Loading {name} details...

+ return ( +

+ Loading {name} details... +

+ ) } const { stargazers, stargazersPagination } = this.props return (
- +
- +
) } @@ -92,7 +97,9 @@ const mapStateToProps = (state, ownProps) => { } } -export default withRouter(connect(mapStateToProps, { - loadRepo, - loadStargazers -})(RepoPage)) +export default withRouter( + connect(mapStateToProps, { + loadRepo, + loadStargazers + })(RepoPage) +) diff --git a/examples/real-world/src/containers/Root.dev.js b/examples/real-world/src/containers/Root.dev.js index e1acc4fa0cb..8b7b8a43c2b 100644 --- a/examples/real-world/src/containers/Root.dev.js +++ b/examples/real-world/src/containers/Root.dev.js @@ -11,17 +11,15 @@ const Root = ({ store }) => (
- - + +
) Root.propTypes = { - store: PropTypes.object.isRequired, + store: PropTypes.object.isRequired } export default Root diff --git a/examples/real-world/src/containers/Root.prod.js b/examples/real-world/src/containers/Root.prod.js index b2b04e5b565..fd1d46f60c1 100644 --- a/examples/real-world/src/containers/Root.prod.js +++ b/examples/real-world/src/containers/Root.prod.js @@ -10,15 +10,13 @@ const Root = ({ store }) => (
- - + +
) Root.propTypes = { - store: PropTypes.object.isRequired, + store: PropTypes.object.isRequired } export default Root diff --git a/examples/real-world/src/containers/UserPage.js b/examples/real-world/src/containers/UserPage.js index 378f4d50116..800c6ef8fb5 100644 --- a/examples/real-world/src/containers/UserPage.js +++ b/examples/real-world/src/containers/UserPage.js @@ -11,7 +11,7 @@ import List from '../components/List' import zip from 'lodash/zip' const loadData = ({ login, loadUser, loadStarred }) => { - loadUser(login, [ 'name' ]) + loadUser(login, ['name']) loadStarred(login) } @@ -40,19 +40,21 @@ class UserPage extends Component { this.props.loadStarred(this.props.login, true) } - renderRepo([ repo, owner ]) { - return ( - - ) + renderRepo([repo, owner]) { + return } render() { const { user, login } = this.props if (!user) { - return

Loading {login}{"'s profile..."}

+ return ( +

+ + Loading {login} + {"'s profile..."} + +

+ ) } const { starredRepos, starredRepoOwners, starredPagination } = this.props @@ -60,11 +62,13 @@ class UserPage extends Component {

- +
) } @@ -93,7 +97,9 @@ const mapStateToProps = (state, ownProps) => { } } -export default withRouter(connect(mapStateToProps, { - loadUser, - loadStarred -})(UserPage)) +export default withRouter( + connect(mapStateToProps, { + loadUser, + loadStarred + })(UserPage) +) diff --git a/examples/real-world/src/middleware/api.js b/examples/real-world/src/middleware/api.js index 54e9741074d..69193e8d788 100644 --- a/examples/real-world/src/middleware/api.js +++ b/examples/real-world/src/middleware/api.js @@ -21,24 +21,23 @@ const API_ROOT = 'https://api.github.com/' // Fetches an API response and normalizes the result JSON according to schema. // This makes every API response have the same shape, regardless of how nested it was. const callApi = (endpoint, schema) => { - const fullUrl = (endpoint.indexOf(API_ROOT) === -1) ? API_ROOT + endpoint : endpoint - - return fetch(fullUrl) - .then(response => - response.json().then(json => { - if (!response.ok) { - return Promise.reject(json) - } - - const camelizedJson = camelizeKeys(json) - const nextPageUrl = getNextPageUrl(response) - - return Object.assign({}, - normalize(camelizedJson, schema), - { nextPageUrl } - ) + const fullUrl = + endpoint.indexOf(API_ROOT) === -1 ? API_ROOT + endpoint : endpoint + + return fetch(fullUrl).then(response => + response.json().then(json => { + if (!response.ok) { + return Promise.reject(json) + } + + const camelizedJson = camelizeKeys(json) + const nextPageUrl = getNextPageUrl(response) + + return Object.assign({}, normalize(camelizedJson, schema), { + nextPageUrl }) - ) + }) + ) } // We use this Normalizr schemas to transform API responses from a nested form @@ -54,15 +53,23 @@ const callApi = (endpoint, schema) => { // leading to a frozen UI as it wouldn't find "someuser" in the entities. // That's why we're forcing lower cases down there. -const userSchema = new schema.Entity('users', {}, { - idAttribute: user => user.login.toLowerCase() -}) - -const repoSchema = new schema.Entity('repos', { - owner: userSchema -}, { - idAttribute: repo => repo.fullName.toLowerCase() -}) +const userSchema = new schema.Entity( + 'users', + {}, + { + idAttribute: user => user.login.toLowerCase() + } +) + +const repoSchema = new schema.Entity( + 'repos', + { + owner: userSchema + }, + { + idAttribute: repo => repo.fullName.toLowerCase() + } +) // Schemas for Github API responses. export const Schemas = { @@ -109,17 +116,23 @@ export default store => next => action => { return finalAction } - const [ requestType, successType, failureType ] = types + const [requestType, successType, failureType] = types next(actionWith({ type: requestType })) return callApi(endpoint, schema).then( - response => next(actionWith({ - response, - type: successType - })), - error => next(actionWith({ - type: failureType, - error: error.message || 'Something bad happened' - })) + response => + next( + actionWith({ + response, + type: successType + }) + ), + error => + next( + actionWith({ + type: failureType, + error: error.message || 'Something bad happened' + }) + ) ) } diff --git a/examples/real-world/src/reducers/index.js b/examples/real-world/src/reducers/index.js index eeb75bf80d3..b2a1a997080 100644 --- a/examples/real-world/src/reducers/index.js +++ b/examples/real-world/src/reducers/index.js @@ -48,7 +48,7 @@ const pagination = combineReducers({ const rootReducer = combineReducers({ entities, pagination, - errorMessage, + errorMessage }) export default rootReducer diff --git a/examples/real-world/src/reducers/paginate.js b/examples/real-world/src/reducers/paginate.js index acabed9804a..ddbbb757edc 100644 --- a/examples/real-world/src/reducers/paginate.js +++ b/examples/real-world/src/reducers/paginate.js @@ -13,14 +13,17 @@ const paginate = ({ types, mapActionToKey }) => { throw new Error('Expected mapActionToKey to be a function.') } - const [ requestType, successType, failureType ] = types + const [requestType, successType, failureType] = types - const updatePagination = (state = { - isFetching: false, - nextPageUrl: undefined, - pageCount: 0, - ids: [] - }, action) => { + const updatePagination = ( + state = { + isFetching: false, + nextPageUrl: undefined, + pageCount: 0, + ids: [] + }, + action + ) => { switch (action.type) { case requestType: return { diff --git a/examples/real-world/src/store/configureStore.dev.js b/examples/real-world/src/store/configureStore.dev.js index 4b4aef4f1b0..e47b2a75774 100644 --- a/examples/real-world/src/store/configureStore.dev.js +++ b/examples/real-world/src/store/configureStore.dev.js @@ -9,10 +9,7 @@ const configureStore = preloadedState => { const store = createStore( rootReducer, preloadedState, - compose( - applyMiddleware(thunk, api, createLogger()), - DevTools.instrument() - ) + compose(applyMiddleware(thunk, api, createLogger()), DevTools.instrument()) ) if (module.hot) { diff --git a/examples/real-world/src/store/configureStore.prod.js b/examples/real-world/src/store/configureStore.prod.js index 9fd1d533ab7..3331b5f7f6b 100644 --- a/examples/real-world/src/store/configureStore.prod.js +++ b/examples/real-world/src/store/configureStore.prod.js @@ -3,10 +3,7 @@ import thunk from 'redux-thunk' import api from '../middleware/api' import rootReducer from '../reducers' -const configureStore = preloadedState => createStore( - rootReducer, - preloadedState, - applyMiddleware(thunk, api) -) +const configureStore = preloadedState => + createStore(rootReducer, preloadedState, applyMiddleware(thunk, api)) export default configureStore diff --git a/examples/shopping-cart/public/index.html b/examples/shopping-cart/public/index.html index 5fb87aa68ae..2f81ed33804 100644 --- a/examples/shopping-cart/public/index.html +++ b/examples/shopping-cart/public/index.html @@ -1,8 +1,8 @@ - + - - + + Redux Shopping Cart Example diff --git a/examples/shopping-cart/src/api/products.json b/examples/shopping-cart/src/api/products.json index 8947ba6c7b4..eb80eb59c89 100644 --- a/examples/shopping-cart/src/api/products.json +++ b/examples/shopping-cart/src/api/products.json @@ -1,5 +1,5 @@ [ - {"id": 1, "title": "iPad 4 Mini", "price": 500.01, "inventory": 2}, - {"id": 2, "title": "H&M T-Shirt White", "price": 10.99, "inventory": 10}, - {"id": 3, "title": "Charli XCX - Sucker CD", "price": 19.99, "inventory": 5} + { "id": 1, "title": "iPad 4 Mini", "price": 500.01, "inventory": 2 }, + { "id": 2, "title": "H&M T-Shirt White", "price": 10.99, "inventory": 10 }, + { "id": 3, "title": "Charli XCX - Sucker CD", "price": 19.99, "inventory": 5 } ] diff --git a/examples/shopping-cart/src/api/shop.js b/examples/shopping-cart/src/api/shop.js index aa979ad4a00..688f92ee735 100644 --- a/examples/shopping-cart/src/api/shop.js +++ b/examples/shopping-cart/src/api/shop.js @@ -6,6 +6,8 @@ import _products from './products.json' const TIMEOUT = 100 export default { - getProducts: (cb, timeout) => setTimeout(() => cb(_products), timeout || TIMEOUT), - buyProducts: (payload, cb, timeout) => setTimeout(() => cb(), timeout || TIMEOUT) + getProducts: (cb, timeout) => + setTimeout(() => cb(_products), timeout || TIMEOUT), + buyProducts: (payload, cb, timeout) => + setTimeout(() => cb(), timeout || TIMEOUT) } diff --git a/examples/shopping-cart/src/components/Cart.js b/examples/shopping-cart/src/components/Cart.js index c9adc2f1e44..0998e83027e 100644 --- a/examples/shopping-cart/src/components/Cart.js +++ b/examples/shopping-cart/src/components/Cart.js @@ -2,17 +2,17 @@ import React from 'react' import PropTypes from 'prop-types' import Product from './Product' -const Cart = ({ products, total, onCheckoutClicked }) => { +const Cart = ({ products, total, onCheckoutClicked }) => { const hasProducts = products.length > 0 const nodes = hasProducts ? ( - products.map(product => + products.map(product => ( - ) + )) ) : ( Please add some products to cart. ) @@ -22,8 +22,10 @@ const Cart = ({ products, total, onCheckoutClicked }) => {

Your Cart

{nodes}

Total: ${total}

-
diff --git a/examples/shopping-cart/src/components/Product.js b/examples/shopping-cart/src/components/Product.js index 4bc0a6eb272..e908163fbc9 100644 --- a/examples/shopping-cart/src/components/Product.js +++ b/examples/shopping-cart/src/components/Product.js @@ -3,7 +3,8 @@ import PropTypes from 'prop-types' const Product = ({ price, quantity, title }) => (
- {title} - ${price}{quantity ? ` x ${quantity}` : null} + {title} - ${price} + {quantity ? ` x ${quantity}` : null}
) diff --git a/examples/shopping-cart/src/components/Product.spec.js b/examples/shopping-cart/src/components/Product.spec.js index efb45235f7a..134bc5e8d14 100644 --- a/examples/shopping-cart/src/components/Product.spec.js +++ b/examples/shopping-cart/src/components/Product.spec.js @@ -3,9 +3,7 @@ import { shallow } from 'enzyme' import Product from './Product' const setup = props => { - const component = shallow( - - ) + const component = shallow() return { component: component @@ -20,7 +18,11 @@ describe('Product component', () => { describe('when given inventory', () => { it('should render title, price, and inventory', () => { - const { component } = setup({ title: 'Test Product', price: 9.99, quantity: 6 }) + const { component } = setup({ + title: 'Test Product', + price: 9.99, + quantity: 6 + }) expect(component.text()).toBe('Test Product - $9.99 x 6') }) }) diff --git a/examples/shopping-cart/src/components/ProductItem.js b/examples/shopping-cart/src/components/ProductItem.js index a84c7a4ed8c..264e4581aef 100644 --- a/examples/shopping-cart/src/components/ProductItem.js +++ b/examples/shopping-cart/src/components/ProductItem.js @@ -7,10 +7,12 @@ const ProductItem = ({ product, onAddToCartClicked }) => ( + quantity={product.inventory} + /> diff --git a/examples/shopping-cart/src/components/ProductItem.spec.js b/examples/shopping-cart/src/components/ProductItem.spec.js index bb85d711649..8011c7316a4 100644 --- a/examples/shopping-cart/src/components/ProductItem.spec.js +++ b/examples/shopping-cart/src/components/ProductItem.spec.js @@ -8,9 +8,7 @@ const setup = product => { onAddToCartClicked: jest.fn() } - const component = shallow( - - ) + const component = shallow() return { component: component, @@ -33,7 +31,11 @@ describe('ProductItem component', () => { it('should render product', () => { const { product } = setup(productProps) - expect(product.props()).toEqual({ title: 'Product 1', price: 9.99, quantity: 6 }) + expect(product.props()).toEqual({ + title: 'Product 1', + price: 9.99, + quantity: 6 + }) }) it('should render Add To Cart message', () => { diff --git a/examples/shopping-cart/src/components/ProductsList.spec.js b/examples/shopping-cart/src/components/ProductsList.spec.js index f24c19e6a75..7ff3177aa15 100644 --- a/examples/shopping-cart/src/components/ProductsList.spec.js +++ b/examples/shopping-cart/src/components/ProductsList.spec.js @@ -21,7 +21,10 @@ describe('ProductsList component', () => { }) it('should render children', () => { - const { children } = setup({ title: 'Test Products', children: 'Test Children' }) + const { children } = setup({ + title: 'Test Products', + children: 'Test Children' + }) expect(children.text()).toMatch(/^Test Children$/) }) }) diff --git a/examples/shopping-cart/src/containers/App.js b/examples/shopping-cart/src/containers/App.js index 7625918633d..53ae377532e 100644 --- a/examples/shopping-cart/src/containers/App.js +++ b/examples/shopping-cart/src/containers/App.js @@ -5,9 +5,9 @@ import CartContainer from './CartContainer' const App = () => (

Shopping Cart Example

-
+
-
+
) diff --git a/examples/shopping-cart/src/containers/CartContainer.js b/examples/shopping-cart/src/containers/CartContainer.js index ac36ff7f14c..2694ea0c7a8 100644 --- a/examples/shopping-cart/src/containers/CartContainer.js +++ b/examples/shopping-cart/src/containers/CartContainer.js @@ -9,26 +9,26 @@ const CartContainer = ({ products, total, checkout }) => ( checkout(products)} /> + onCheckoutClicked={() => checkout(products)} + /> ) CartContainer.propTypes = { - products: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.number.isRequired, - title: PropTypes.string.isRequired, - price: PropTypes.number.isRequired, - quantity: PropTypes.number.isRequired - })).isRequired, + products: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.number.isRequired, + title: PropTypes.string.isRequired, + price: PropTypes.number.isRequired, + quantity: PropTypes.number.isRequired + }) + ).isRequired, total: PropTypes.string, checkout: PropTypes.func.isRequired } -const mapStateToProps = (state) => ({ +const mapStateToProps = state => ({ products: getCartProducts(state), total: getTotal(state) }) -export default connect( - mapStateToProps, - { checkout } -)(CartContainer) +export default connect(mapStateToProps, { checkout })(CartContainer) diff --git a/examples/shopping-cart/src/containers/ProductsContainer.js b/examples/shopping-cart/src/containers/ProductsContainer.js index 9108a97a721..6ed91601ca3 100644 --- a/examples/shopping-cart/src/containers/ProductsContainer.js +++ b/examples/shopping-cart/src/containers/ProductsContainer.js @@ -8,22 +8,25 @@ import ProductsList from '../components/ProductsList' const ProductsContainer = ({ products, addToCart }) => ( - {products.map(product => + {products.map(product => ( addToCart(product.id)} /> - )} + onAddToCartClicked={() => addToCart(product.id)} + /> + ))} ) ProductsContainer.propTypes = { - products: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.number.isRequired, - title: PropTypes.string.isRequired, - price: PropTypes.number.isRequired, - inventory: PropTypes.number.isRequired - })).isRequired, + products: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.number.isRequired, + title: PropTypes.string.isRequired, + price: PropTypes.number.isRequired, + inventory: PropTypes.number.isRequired + }) + ).isRequired, addToCart: PropTypes.func.isRequired } @@ -31,7 +34,4 @@ const mapStateToProps = state => ({ products: getVisibleProducts(state.products) }) -export default connect( - mapStateToProps, - { addToCart } -)(ProductsContainer) +export default connect(mapStateToProps, { addToCart })(ProductsContainer) diff --git a/examples/shopping-cart/src/index.js b/examples/shopping-cart/src/index.js index d1662644ade..c8f6dd83d05 100644 --- a/examples/shopping-cart/src/index.js +++ b/examples/shopping-cart/src/index.js @@ -8,15 +8,12 @@ import reducer from './reducers' import { getAllProducts } from './actions' import App from './containers/App' -const middleware = [ thunk ]; +const middleware = [thunk] if (process.env.NODE_ENV !== 'production') { - middleware.push(createLogger()); + middleware.push(createLogger()) } -const store = createStore( - reducer, - applyMiddleware(...middleware) -) +const store = createStore(reducer, applyMiddleware(...middleware)) store.dispatch(getAllProducts()) diff --git a/examples/shopping-cart/src/reducers/cart.js b/examples/shopping-cart/src/reducers/cart.js index 7d87e42d015..949cf818454 100644 --- a/examples/shopping-cart/src/reducers/cart.js +++ b/examples/shopping-cart/src/reducers/cart.js @@ -15,7 +15,7 @@ const addedIds = (state = initialState.addedIds, action) => { if (state.indexOf(action.productId) !== -1) { return state } - return [ ...state, action.productId ] + return [...state, action.productId] default: return state } @@ -25,9 +25,7 @@ const quantityById = (state = initialState.quantityById, action) => { switch (action.type) { case ADD_TO_CART: const { productId } = action - return { ...state, - [productId]: (state[productId] || 0) + 1 - } + return { ...state, [productId]: (state[productId] || 0) + 1 } default: return state } diff --git a/examples/shopping-cart/src/reducers/cart.spec.js b/examples/shopping-cart/src/reducers/cart.spec.js index e2e2bd7fd85..428afa6a18f 100644 --- a/examples/shopping-cart/src/reducers/cart.spec.js +++ b/examples/shopping-cart/src/reducers/cart.spec.js @@ -16,25 +16,29 @@ describe('reducers', () => { }) it('should handle CHECKOUT_FAILURE action', () => { - expect(cart({}, { type: 'CHECKOUT_FAILURE', cart: 'cart state' })).toEqual('cart state') + expect( + cart({}, { type: 'CHECKOUT_FAILURE', cart: 'cart state' }) + ).toEqual('cart state') }) it('should handle ADD_TO_CART action', () => { - expect(cart(initialState, { type: 'ADD_TO_CART', productId: 1 })).toEqual({ - addedIds: [ 1 ], - quantityById: { 1: 1 } - }) + expect(cart(initialState, { type: 'ADD_TO_CART', productId: 1 })).toEqual( + { + addedIds: [1], + quantityById: { 1: 1 } + } + ) }) describe('when product is already in cart', () => { it('should handle ADD_TO_CART action', () => { const state = { - addedIds: [ 1, 2 ], + addedIds: [1, 2], quantityById: { 1: 1, 2: 1 } } expect(cart(state, { type: 'ADD_TO_CART', productId: 2 })).toEqual({ - addedIds: [ 1, 2 ], + addedIds: [1, 2], quantityById: { 1: 1, 2: 2 } }) }) diff --git a/examples/shopping-cart/src/reducers/index.js b/examples/shopping-cart/src/reducers/index.js index 3d31afdab20..6ddaa4b0c2c 100644 --- a/examples/shopping-cart/src/reducers/index.js +++ b/examples/shopping-cart/src/reducers/index.js @@ -13,8 +13,9 @@ const getProduct = (state, id) => fromProducts.getProduct(state.products, id) export const getTotal = state => getAddedIds(state) - .reduce((total, id) => - total + getProduct(state, id).price * getQuantity(state, id), + .reduce( + (total, id) => + total + getProduct(state, id).price * getQuantity(state, id), 0 ) .toFixed(2) diff --git a/examples/shopping-cart/src/reducers/index.spec.js b/examples/shopping-cart/src/reducers/index.spec.js index c49c2d3baf7..7ed85a4ed68 100644 --- a/examples/shopping-cart/src/reducers/index.spec.js +++ b/examples/shopping-cart/src/reducers/index.spec.js @@ -5,7 +5,7 @@ describe('selectors', () => { it('should return price total', () => { const state = { cart: { - addedIds: [ 1, 2, 3 ], + addedIds: [1, 2, 3], quantityById: { 1: 4, 2: 2, @@ -37,7 +37,7 @@ describe('selectors', () => { it('should return products with quantity', () => { const state = { cart: { - addedIds: [ 1, 2, 3 ], + addedIds: [1, 2, 3], quantityById: { 1: 4, 2: 2, diff --git a/examples/shopping-cart/src/reducers/products.js b/examples/shopping-cart/src/reducers/products.js index 01dba2c102d..be2b9e00881 100644 --- a/examples/shopping-cart/src/reducers/products.js +++ b/examples/shopping-cart/src/reducers/products.js @@ -49,8 +49,7 @@ export default combineReducers({ visibleIds }) -export const getProduct = (state, id) => - state.byId[id] +export const getProduct = (state, id) => state.byId[id] export const getVisibleProducts = state => state.visibleIds.map(id => getProduct(state, id)) diff --git a/examples/shopping-cart/src/reducers/products.spec.js b/examples/shopping-cart/src/reducers/products.spec.js index 105d3bacd3d..043e629d81a 100644 --- a/examples/shopping-cart/src/reducers/products.spec.js +++ b/examples/shopping-cart/src/reducers/products.spec.js @@ -5,39 +5,41 @@ describe('reducers', () => { let state describe('when products are received', () => { - beforeEach(() => { - state = reducer({}, { - type: 'RECEIVE_PRODUCTS', - products: [ - { - id: 1, - title: 'Product 1', - inventory: 2 - }, - { - id: 2, - title: 'Product 2', - inventory: 1 - } - ] - }) + state = reducer( + {}, + { + type: 'RECEIVE_PRODUCTS', + products: [ + { + id: 1, + title: 'Product 1', + inventory: 2 + }, + { + id: 2, + title: 'Product 2', + inventory: 1 + } + ] + } + ) }) it('contains the products from the action', () => { expect(products.getProduct(state, 1)).toEqual({ id: 1, title: 'Product 1', - inventory: 2 + inventory: 2 }) expect(products.getProduct(state, 2)).toEqual({ id: 2, title: 'Product 2', - inventory: 1 + inventory: 1 }) }) - it ('contains no other products', () => { + it('contains no other products', () => { expect(products.getProduct(state, 3)).toEqual(undefined) }) @@ -47,7 +49,8 @@ describe('reducers', () => { id: 1, title: 'Product 1', inventory: 2 - }, { + }, + { id: 2, title: 'Product 2', inventory: 1 @@ -56,7 +59,6 @@ describe('reducers', () => { }) describe('when an item is added to the cart', () => { - beforeEach(() => { state = reducer(state, { type: 'ADD_TO_CART', productId: 1 }) }) @@ -67,16 +69,15 @@ describe('reducers', () => { id: 1, title: 'Product 1', inventory: 1 - }, { + }, + { id: 2, title: 'Product 2', inventory: 1 } ]) }) - }) - }) }) }) diff --git a/examples/testAll.js b/examples/testAll.js index 260e5781f5d..9b74672cd59 100644 --- a/examples/testAll.js +++ b/examples/testAll.js @@ -12,7 +12,10 @@ const exampleDirs = fs.readdirSync(__dirname).filter(file => { }) // Ordering is important here. `npm install` must come first. -const cmdArgs = [{ cmd: 'npm', args: ['ci'] }, { cmd: 'npm', args: ['test'] }] +const cmdArgs = [ + { cmd: 'npm', args: ['ci'] }, + { cmd: 'npm', args: ['test'] } +] for (const dir of exampleDirs) { if (dir === 'counter-vanilla' || dir === 'universal') continue @@ -34,7 +37,11 @@ for (const dir of exampleDirs) { } if (result.status !== 0) { console.log(result) - throw new Error(`Building examples exited with non-zero ${opts.cwd} cmd: ${JSON.stringify(cmdArg)}`) + throw new Error( + `Building examples exited with non-zero ${ + opts.cwd + } cmd: ${JSON.stringify(cmdArg)}` + ) } } } diff --git a/examples/todomvc/public/index.html b/examples/todomvc/public/index.html index 78bccfeeca2..2a7275ca4b2 100644 --- a/examples/todomvc/public/index.html +++ b/examples/todomvc/public/index.html @@ -1,8 +1,8 @@ - + - - + + Redux TodoMVC Example diff --git a/examples/todomvc/src/actions/index.js b/examples/todomvc/src/actions/index.js index 9b3e82527d9..254c2795447 100644 --- a/examples/todomvc/src/actions/index.js +++ b/examples/todomvc/src/actions/index.js @@ -6,4 +6,7 @@ export const editTodo = (id, text) => ({ type: types.EDIT_TODO, id, text }) export const completeTodo = id => ({ type: types.COMPLETE_TODO, id }) export const completeAllTodos = () => ({ type: types.COMPLETE_ALL_TODOS }) export const clearCompleted = () => ({ type: types.CLEAR_COMPLETED }) -export const setVisibilityFilter = filter => ({ type: types.SET_VISIBILITY_FILTER, filter}) +export const setVisibilityFilter = filter => ({ + type: types.SET_VISIBILITY_FILTER, + filter +}) diff --git a/examples/todomvc/src/components/Footer.js b/examples/todomvc/src/components/Footer.js index cca356538e8..ebf21b28011 100644 --- a/examples/todomvc/src/components/Footer.js +++ b/examples/todomvc/src/components/Footer.js @@ -9,7 +9,7 @@ const FILTER_TITLES = { [SHOW_COMPLETED]: 'Completed' } -const Footer = (props) => { +const Footer = props => { const { activeCount, completedCount, onClearCompleted } = props const itemWord = activeCount === 1 ? 'item' : 'items' return ( @@ -18,22 +18,17 @@ const Footer = (props) => { {activeCount || 'No'} {itemWord} left
    - {Object.keys(FILTER_TITLES).map(filter => + {Object.keys(FILTER_TITLES).map(filter => (
  • - - {FILTER_TITLES[filter]} - + {FILTER_TITLES[filter]}
  • - )} + ))}
- { - !!completedCount && - - - } + {!!completedCount && ( + + )} ) } @@ -41,7 +36,7 @@ const Footer = (props) => { Footer.propTypes = { completedCount: PropTypes.number.isRequired, activeCount: PropTypes.number.isRequired, - onClearCompleted: PropTypes.func.isRequired, + onClearCompleted: PropTypes.func.isRequired } export default Footer diff --git a/examples/todomvc/src/components/Footer.spec.js b/examples/todomvc/src/components/Footer.spec.js index af888b251f3..b58dc7e41b7 100644 --- a/examples/todomvc/src/components/Footer.spec.js +++ b/examples/todomvc/src/components/Footer.spec.js @@ -1,15 +1,18 @@ import React from 'react' -import { createRenderer } from 'react-test-renderer/shallow'; +import { createRenderer } from 'react-test-renderer/shallow' import Footer from './Footer' import FilterLink from '../containers/FilterLink' import { SHOW_ALL, SHOW_ACTIVE, SHOW_COMPLETED } from '../constants/TodoFilters' const setup = propOverrides => { - const props = Object.assign({ - completedCount: 0, - activeCount: 0, - onClearCompleted: jest.fn(), - }, propOverrides) + const props = Object.assign( + { + completedCount: 0, + activeCount: 0, + onClearCompleted: jest.fn() + }, + propOverrides + ) const renderer = createRenderer() renderer.render(