import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
export const fetchUser = createAsyncThunk('user/fetch', async (id: number) => {
const res = await fetch(`/api/users/${id}`);
return res.json();
});
interface CounterState { value: number; status: 'idle' | 'loading' | 'failed' }
const initialState: CounterState = { value: 0, status: 'idle' };
const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment: (state) => { state.value += 1; },
decrement: (state) => { state.value -= 1; },
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload;
},
},
extraReducers: (builder) => {
builder
.addCase(fetchUser.pending, (state) => { state.status = 'loading'; })
.addCase(fetchUser.fulfilled, (state) => { state.status = 'idle'; })
.addCase(fetchUser.rejected, (state) => { state.status = 'failed'; });
},
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
import { configureStore } from '@reduxjs/toolkit';
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import counterReducer from './counterSlice';
export const store = configureStore({
reducer: { counter: counterReducer },
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
import { Provider } from 'react-redux';
function Main() {
return <Provider store={store}><App /></Provider>;
}
function Counter() {
const count = useAppSelector((state) => state.counter.value);
const dispatch = useAppDispatch();
return (
<div>
<button onClick={() => dispatch(decrement())}>-</button>
<span>{count}</span>
<button onClick={() => dispatch(increment())}>+</button>
<button onClick={() => dispatch(incrementByAmount(5))}>+5</button>
</div>
);
}