Last active
December 8, 2021 15:03
-
-
Save on3iro/ef7ba74586e34002aae908fbb9b78f9b to your computer and use it in GitHub Desktop.
december 2021 redux training
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
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:
- Lies in
PostListing
nur noch die Post-Ids statt der Posts aus dem Store aus - Mappe dann in
PostListing
über die Ids - Die
PostListingItem
-Komponente soll nun als Props statt einesPost
dessenid
erwarten - Erstelle einen primitiven Selektor
getPostById()
, welcher aus dem State einen Post anhand dessenid
ausliest
und verwende diesen in derPostListingItem
-Komponente.
8. Post löschen
- Analog zu Aufgabe 6 soll nun die
deletePost()
-Funktion aussrc/model/Post.ts
durch einen Thunk ersetzt werden. - Das Post state soll erst aktualisiert werden, wenn das Request erfolgreich war.
Hinweis:
rejected
undpending
müssen hier vorerst nicht gehandled werden
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,
}
})
9. Post editieren
- Sorge dafür dass der Post zum editeren nicht mehr neu gefetched werden muss
Hinweis: Gehe dabei analog zu Aufgabe 7 vor.
Dieid
kommt in diesem Fall allerdings aus der Route, welche viaprops.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) => {
...
}
- Ersetze die
updatePost
-Funktion aussrc/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.
10. Erstellen eines neuen Posts
- Ersetze nun auch die
postPost
-Funktion aussrc/model/Post
durch einen entsprechenden Thunk
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
6. Seiteneffekthandling: Posts fetchen
Erstelle einen thunk action creator mit
createAsyncThunk()
namensfetchPosts
,welcher analog zu
fetchPosts()
insrc/model/Post.ts
alle posts fetched.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))
Erstelle ein weiteres slice
postsLoading
insrc/store/posts/loading.ts
mit folgenden Eigenschaften:Erstelle einen primitiven Selektor, welcher das PostLoadingState ausliest
Stelle sicher, dass der loading reducer auch im kombinierten
RootReducer
verwendet wirdReagiere im
postsLoading
slice auf diefetchPosts()
Action-types:(
pending
-> truefulfilled
-> falserejected
-> false)
Ersetze in
PostListing.tsx
das loading state durchpostsLoading
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)