Skip to content

Instantly share code, notes, and snippets.

@on3iro
Last active December 8, 2021 15:03
Show Gist options
  • Save on3iro/ef7ba74586e34002aae908fbb9b78f9b to your computer and use it in GitHub Desktop.
Save on3iro/ef7ba74586e34002aae908fbb9b78f9b to your computer and use it in GitHub Desktop.
december 2021 redux training
import { applyMiddleware, createStore } from 'redux'
import thunkMiddleware from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'
import { postReducer } from './posts/posts'
import { combineReducers } from '@reduxjs/toolkit'
export function configureStore() {
const middlewares = [thunkMiddleware]
const middlewareEnhancer = applyMiddleware(...middlewares)
const enhancers = [middlewareEnhancer]
const composedEnhancers = composeWithDevTools(...enhancers)
const store = createStore(combineReducers({ posts: postReducer }), undefined, composedEnhancers)
return store
}
import React from 'react'
import ReactDOM from 'react-dom'
import './index.scss'
import App from './components/App/App'
import reportWebVitals from './reportWebVitals'
import { configureStore } from './store/configureStore'
export const store = configureStore()
// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
)
// 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()
@on3iro
Copy link
Author

on3iro commented Dec 7, 2021

@on3iro
Copy link
Author

on3iro commented Dec 7, 2021

5. Einführung von Redux-Toolkit und Vereinfachung des Stores

  1. Ersetze die selbstgeschriebene configureStore() funktion, durch einen Aufruf von Redux-Toolkits configureStore()
  2. Schreibe das Post slice so um, dass es createSlice() von Redux-Toolkit verwendet

@on3iro
Copy link
Author

on3iro commented Dec 8, 2021

6. Seiteneffekthandling: Posts fetchen

  1. Erstelle einen thunk action creator mit createAsyncThunk() namens fetchPosts,
    welcher analog zu fetchPosts() in src/model/Post.ts alle posts fetched.

  2. Handle alle Action-Types des thunks im postReducer:
    (
    pending -> aktuelles State returnen,
    fulfilled -> Postliste aus der response returnen,
    rejected -> aktuellen State returnen (wir handlen den Fehler vorerst nicht)
    )

  3. Erstelle ein weiteres slice postsLoading in src/store/posts/loading.ts mit folgenden Eigenschaften:

    type PostLoadingState = boolean
    
    const initialState: PostLoadingState = false

Hinweis: Lasse die reducers-property des slices vorerst leer

  1. Erstelle einen primitiven Selektor, welcher das PostLoadingState ausliest

  2. Stelle sicher, dass der loading reducer auch im kombinierten RootReducer verwendet wird

  3. Reagiere im postsLoading slice auf die fetchPosts() Action-types:
    (
    pending -> true
    fulfilled -> false
    rejected -> false
    )

  4. Ersetze in PostListing.tsx das loading state durch postsLoading aus dem Redux-Store.
    Ersetze außerdem das fetching durch einen Aufruf des fetchPosts thunk creators.
    Sorge zudem dafür, dass der fetch useEffect nur beim ersten Rendern der Komponente aufgerufen wird.
    (Da wir die Posts jetzt im Application-State halten, muss nicht jedesmal neu gefetched werden)

    Hinweis: Dadurch, dass wir die Posts nun nur noch beim ersten Rendering fetchen, werden einige Funktionen
    unserer App vorerst nicht korrekt funktionieren (bspw. postPost).

@on3iro
Copy link
Author

on3iro commented Dec 8, 2021

7. Post via Selektor auslesen

Derzeit werden in unserem Post-Listing alle Posts aus dem Store gelesen und einfach auf Item-Komponenten gemapped.
Nun soll das ganze folgendermaßen umgebaut werden:

  1. Lies in PostListing nur noch die Post-Ids statt der Posts aus dem Store aus
  2. Mappe dann in PostListing über die Ids
  3. Die PostListingItem-Komponente soll nun als Props statt eines Post dessen id erwarten
  4. Erstelle einen primitiven Selektor getPostById(), welcher aus dem State einen Post anhand dessen id ausliest
    und verwende diesen in der PostListingItem-Komponente.

@on3iro
Copy link
Author

on3iro commented Dec 8, 2021

8. Post löschen

  1. Analog zu Aufgabe 6 soll nun die deletePost()-Funktion aus src/model/Post.ts durch einen Thunk ersetzt werden.
  2. Das Post state soll erst aktualisiert werden, wenn das Request erfolgreich war.

Hinweis: rejected und pending müssen hier vorerst nicht gehandled werden

@JamesAlias
Copy link

Immutable remove:

 .addCase(deletePost.fulfilled, (state, action) => {
        const { id } = action.payload
        const { 
          [id]: postToRemove, // const _ = state.byId[id] <= dynamic access of element via key (id)
          ...byId // rest operator -> all remaining elements in object are put into new obj with the name "byId"
        } = state.byId

        console.log('hi, I deleted this post for you:', postToRemove)

        return {
          ids: state.ids.filter((postId) => postId !== id),
          byId,
        }
      })

@on3iro
Copy link
Author

on3iro commented Dec 8, 2021

9. Post editieren

  1. Sorge dafür dass der Post zum editeren nicht mehr neu gefetched werden muss

Hinweis: Gehe dabei analog zu Aufgabe 7 vor.
Die id kommt in diesem Fall allerdings aus der Route, welche via props.match.params.id ausgelesen werden kann.
Hier kann die Typisierung etwas knifflig sein, um die Props zu typisieren, könnt ihr folgendermaßen vorgehen:

type RouteProps = {
  id: string
}
 
type OwnProps = {
  postId: Post['id']
 } & RouteComponentProps<RouteProps>

 const mapStateToProps = (state: RootState, ownProps: OwnProps) => {
 const postId = ownProps.match.params.id

  return {
    post: selectors.getPostById(state, { postId }),
  }
}

const mapDispatchToProps = {
  updatePost,
}

const connector = connect(mapStateToProps, mapDispatchToProps)

type PropsFromRedux = ConnectedProps<typeof connector>
type Props = PropsFromRedux & OwnProps

const PostEditorView = (props: Props) => {
   ...
 }
  1. Ersetze die updatePost-Funktion aus src/model/Post durch einen entsprechend Thunk in
    src/store/posts/posts. Ist das request erfolgreich, soll der Store mit der Antwort des Servers aktualisiert werden.

Hinweis: Das Transient-Post state wird nicht mehr benötigt.

@on3iro
Copy link
Author

on3iro commented Dec 8, 2021

10. Erstellen eines neuen Posts

  1. Ersetze nun auch die postPost-Funktion aus src/model/Post durch einen entsprechenden Thunk

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment