Last active
October 26, 2018 17:20
-
-
Save itszero/6fa54d6c16f1e166fe125512129aea60 to your computer and use it in GitHub Desktop.
Attempt to recreate react-redux using hooks
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 * as React from "react"; | |
import { useContext, useEffect, useState } from "react"; | |
import { render } from "react-dom"; | |
import { applyMiddleware, bindActionCreators, createStore } from "redux"; | |
import ReduxThunk from "redux-thunk"; | |
import { createSelector, createStructuredSelector } from "reselect"; | |
import { action, getType } from "typesafe-actions"; | |
interface Post { | |
id: number; | |
title: string; | |
body: string; | |
} | |
interface Comment { | |
id: number; | |
name: string; | |
email: string; | |
body: string; | |
} | |
interface State { | |
readonly posts: Post[]; | |
readonly comments: { | |
[key: number]: Comment, | |
}; | |
} | |
const initialState: State = { posts: [], comments: {} }; | |
const ADD_POSTS = "posts/ADD_POSTS"; | |
const addPosts = (posts: Post[]) => action(ADD_POSTS, posts); | |
const ADD_COMMENTS = "posts/ADD_COMMENTS"; | |
const addComments = (postId: number, comments: Comment[]) => action(ADD_COMMENTS, comments, { postId }); | |
function fetchPosts() { | |
return (dispatch) => { | |
fetch("https://jsonplaceholder.typicode.com/posts") | |
.then((resp) => resp.json()) | |
.then((data) => dispatch(addPosts(data))); | |
}; | |
} | |
function fetchComments(postId: number) { | |
return (dispatch) => { | |
fetch(`https://jsonplaceholder.typicode.com/comments?postId=${postId}`) | |
.then((resp) => resp.json()) | |
.then((data) => dispatch(addComments(postId, data))); | |
}; | |
} | |
const rootReducer = (state: State, action): State => { | |
switch (action.type) { | |
case ADD_POSTS: | |
return { | |
...state, | |
posts: [...state.posts, ...action.payload], | |
}; | |
break; | |
case ADD_COMMENTS: | |
return { | |
...state, | |
comments: { | |
...state.comments, | |
[action.meta.postId]: action.payload, | |
}, | |
}; | |
break; | |
default: | |
return state; | |
} | |
}; | |
const store = createStore(rootReducer, initialState, applyMiddleware(ReduxThunk)); | |
const ReduxProvider = React.createContext(); | |
function useRedux(mapStateToProps, mapDispatchToProps) { | |
const store = useContext(ReduxProvider); | |
const [state, setState] = useState(mapStateToProps(store.getState())); | |
useEffect(() => { | |
return store.subscribe(() => { | |
setState(mapStateToProps(store.getState())); | |
}); | |
}, [mapStateToProps]); | |
return [state, bindActionCreators(mapDispatchToProps, store.dispatch)]; | |
} | |
const selectPosts = (state) => state.posts; | |
const selectComments = (state) => state.comments; | |
function App() { | |
const [{ posts, comments }, actions] = useRedux(createStructuredSelector({ | |
posts: selectPosts, | |
comments: selectComments, | |
}), { fetchPosts, fetchComments }); | |
return ( | |
<div> | |
{posts.length === 0 && <button onClick={actions.fetchPosts}>Load Posts</button>} | |
<ul> | |
{posts.map((post) => ( | |
<li key={post.id}> | |
<p>{post.title}</p> | |
{ comments[post.id] ? ( | |
<ul> | |
{comments[post.id].map((comment) => ( | |
<li key={comment.id}> | |
{comment.name}({comment.email}): {comment.body} | |
</li> | |
))} | |
</ul> | |
) : ( | |
<button onClick={() => actions.fetchComments(post.id)}>Load Comments</button> | |
) } | |
</li> | |
))} | |
</ul> | |
</div> | |
); | |
} | |
render(<ReduxProvider.Provider value={store}><App/></ReduxProvider.Provider>, document.getElementById("root")); |
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
function useRedux(mapStateToProps, mapDispatchToProps) { | |
const store = useContext(ReduxProvider); | |
const [state, setState] = useState(mapStateToProps(store.getState())); | |
useEffect(() => { | |
return store.subscribe(() => { | |
setState(mapStateToProps(store.getState())); | |
}); | |
}, [mapStateToProps]); | |
return [state, bindActionCreators(mapDispatchToProps, store.dispatch)]; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment