-
-
Save on3iro/ef7ba74586e34002aae908fbb9b78f9b to your computer and use it in GitHub Desktop.
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() |
5. Einführung von Redux-Toolkit und Vereinfachung des Stores
- Ersetze die selbstgeschriebene
configureStore()
funktion, durch einen Aufruf von Redux-ToolkitsconfigureStore()
- Schreibe das
Post
slice so um, dass escreateSlice()
von Redux-Toolkit verwendet
6. Seiteneffekthandling: Posts fetchen
-
Erstelle einen thunk action creator mit
createAsyncThunk()
namensfetchPosts
,
welcher analog zufetchPosts()
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:type PostLoadingState = boolean const initialState: PostLoadingState = false
Hinweis: Lasse die
reducers
-property des slices vorerst leer
-
Erstelle einen primitiven Selektor, welcher das PostLoadingState ausliest
-
Stelle sicher, dass der loading reducer auch im kombinierten
RootReducer
verwendet wird -
Reagiere im
postsLoading
slice auf diefetchPosts()
Action-types:
(
pending
-> true
fulfilled
-> false
rejected
-> false
) -
Ersetze in
PostListing.tsx
das loading state durchpostsLoading
aus dem Redux-Store.
Ersetze außerdem das fetching durch einen Aufruf desfetchPosts
thunk creators.
Sorge zudem dafür, dass der fetchuseEffect
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
).
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
4. Einführung von Selektoren
PostListing
PostListing
-Komponente undconsole.log()
ihn (er muss nicht via JSX gerendert werden)