GenRate Redux
GenRate Redux package aims simplify redux implementation
Install
npm install @genrate/redux
Usage
Slice
import { model, as } from '@genrate/redux'
import { PayloadAction } from '@reduxjs/toolkit';
const state = {
email: as<string>('test@sample.com'), // required
password: as<string>(), // optional
remember: as<boolean | undefined>(false), // optional with default
profile: {
name: as<string>();
hobbies: as<string[]>();
}
}
export type UserState = typeof state;
export default model(
'user', // slice name
state, // slice state
{
// reducers
set(state, action: PayloadAction<UserState>) {
Object.assign(state, action.payload)
}
}, {
// selectors
isPlayingBasketball: (state) => state.profile?.hobbies?.indexOf('basketball') > -1
}
)
Nested Slice
import { model, as, asModelList, StateType } from '@genrate/redux'
import { PayloadAction } from '@reduxjs/toolkit';
const commentState = {
message: as<string>(),
likes: as<number>(0)
};
const Comment = model('comment', commentState, {
set(state, action: PayloadAction<string>) {
state.message = action.payload
},
addLike(state) {
state.likes += 1
}
})
const postState = {
content: as<string>(),
newCommentStatus: as<string>('idle'),
comments: asModelList(Comment, []) // as type model array
}
type PostState = StateType<typeof postState>
const Post = model('post', postState,
// ReducerCreators
({ reducer, asyncThunk }) => ({
set: reducer<string>(state, { payload }) {
state.content = payload
},
addComment: asyncThunk( // async reducer
async (comment: string) => {
const response = await apiAddComment(comment)
return response.data
},
{
pending: state => {
state.newCommentStatus = "loading"
},
fulfilled: (state, action) => {
state.newCommentStatus = "idle"
state.message = action.payload
},
rejected: state => {
state.newCommentStatus = "failed"
},
},
)
// selectors
}), {
commentsWithLikes: (state) => state.comments.filter(c => c.likes > 0)
}
)
// usage in react
const Post = () => {
const content = Post.useContent()
const comments = Post.useCommentsWithLikes();
const addComment = Post.useAddComment();
return (
<div>
<span>
{content}
</span>
{comments.map(
(comment, i) => (
<div key={i}>
<button onClick={() => comment.addLike()} /> // inherit model actions
<span>
{comment.message}
</span>
</div>
)
)}
</div>
)
}
Selector
import { select, arg } from '@genrate/redux'
import User from './models/user'
const getProfileName = select([User.profile], (profile) => profile.name);
// selector with arguments
const hasHobby = select(
[User.profile.hobbies],
[arg<string>(1)],
(hobbies, hobby) => hobbies.find(h => h == hobby);
)
// using on react
const name = useSelector(getProfile); //
const name = getProfile.useSelect();
// with arguments
const isPlayingBadminton = useSelector(state => hasHobby(state, 'badminton'));
const isPlayingBasketball = hasHobby.useSelect('basketball');
Slice in react
import User from './models/user'
const Component = () => {
// auto memoized selector
const user = User.useAll(); // eq = useSelector(state => state.user)
// deep selector
// sampe as
// cachedUser = (state) => state.user;
// cachedProfile = createSelector([main], state => state.profile)
// cachedName = createSelector([profile], state => state.name)
// deep = useSelector(name);
const name = User.profile.useName()
// get action with dispatch
const setUser = User.useSet();
return (
<div>
<span> {user && user.email} </span>
<button onClick={() => setUser({ email: 'test@gmail' })} />
<div>
)
}
RTX Query
import { fetch } from '@genrate/redux'
const { api, get, post } = fetch('posts')
type User = {
id: number,
name: string
}
const UserApi = api({
getOne: get<User, number>((id) => `users/${id}`),
update: post<User, Partial<User>>((update) => ({ url: `users/${id}`, body: update }))
// test: get<User, number>(
// (id) => `users/${id}`, {
// transform: (res) => res.data,
// tags: (_post, _err, id) => [{ type: 'Posts', id: }] // provideTags
// }
// )
})
function Component () => {
const [user, { isFetching }] = UserApi.useGetQuery(1);
const [updateUser, { isLoading }] = UserApi.useUpdateMutation())
return (
<div>
{isFetching ? 'Loading' : user.name }
<button onClick={() => updateUser({ name: 'test' })} />
</div>
)
}