I'm slowly migrating over from Redux to Redux toolkit. I'm still pretty new but I have this login action function. How can I translate old function below do I need createAsyncThunk to achieve this?
export const login = (email, password) => (dispatch) => {
dispatch(requestLogin());
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then((user) => {
dispatch(responseLogin(user));
})
.catch((error) => {
dispatch(loginError());
});
};
and my auth slice looks something like this:
const authSlice = createSlice({
name: "authSlice",
initialState: {
isLoggingIn: false,
isLoggingOut: false,
isVerifying: false,
loginError: false,
logoutError: false,
isAuthenticated: false,
user: {},
},
reducers: {
signInWithEmail: (state, action) => {
const { email, password } = action.payload;
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then((response) => {
const {
uid,
email,
emailVerified,
phoneNumber,
password,
displayName,
photoURL,
} = response.user;
})
.catch((error) => {
console.log(error);
});
},
},
extraReducers: {},
});
Lets create a productSlice.js
import { createSlice,createSelector,PayloadAction,createAsyncThunk,} from "#reduxjs/toolkit";
export const fetchProducts = createAsyncThunk(
"products/fetchProducts", async (_, thunkAPI) => {
try {
const response = await fetch(`url`); //where you want to fetch data
return await response.json();
} catch (error) {
return thunkAPI.rejectWithValue({ error: error.message });
}
});
const productsSlice = createSlice({
name: "products",
initialState: {
products: [],
loading: "idle",
error: "",
},
reducers: {},
extraReducers: (builder) => {
builder.addCase(fetchProducts.pending, (state) => {
state. products = [];
state.loading = "loading";
});
builder.addCase(
fetchProducts.fulfilled, (state, { payload }) => {
state. products = payload;
state.loading = "loaded";
});
builder.addCase(
fetchProducts.rejected,(state, action) => {
state.loading = "error";
state.error = action.error.message;
});
}
});
export const selectProducts = createSelector(
(state) => ({
products: state.products,
loading: state.products.loading,
}), (state) => state
);
export default productsSlice;
In your store.js add productsSlice: productsSlice.reducer in out store reducer.
Then for using in component add those code ... I'm also prefer to use hook
import { useSelector, useDispatch } from "react-redux";
import { fetchProducts,selectProducts,} from "path/productSlice.js";
Then Last part calling those method inside your competent like this
const dispatch = useDispatch();
const { products } = useSelector(selectProducts);
React.useEffect(() => {
dispatch(fetchProducts());
}, [dispatch]);
And Finally, you can access data as products in your component.
The reducer you showed is very wrong. Reducers must never do anything async!
You don't need createAsyncThunk, but if you want to use it, it'd be like this:
export const login = createAsyncThunk(
'login',
({email, password}) => firebase.auth().signInWithEmailAndPassword(email, password)
);
const authSlice = createSlice({
name: "authSlice",
initialState: {
isLoggingIn: false,
isLoggingOut: false,
isVerifying: false,
loginError: false,
logoutError: false,
isAuthenticated: false,
user: {},
},
reducers: {
/* any other state updates here */
},
extraReducers: (builder) => {
builder.addCase(login.pending, (state, action) => {
// mark something as loading here
}
builder.addCase(login.fulfilled, (state, action) => {
// mark request as complete and save results
}
}
});
Note that createAsyncThunk only allows one argument to be passed to the thunk action creator, so it now must be an object with both fields instead of separate arguments.
Related
I am trying to store the token in local store and try to get token value when the user open app but i get undefined while getting the app in redux-toolkit anyone please help me with this
import {createSlice, PayloadAction} from '#reduxjs/toolkit';
import {User, LoginPayload, LoginPayloadError} from './interface';
import {login} from '../../service/authService';
import AsyncStorage from '#react-native-async-storage/async-storage';
let savedToken;
const getData = async () => {
savedToken = await AsyncStorage.getItem('token');
};
getData();
console.log(savedToken);
const initialState: User = {
token: savedToken ? savedToken : '',
id: 0,
email: '',
nicename: '',
firstName: '',
lastName: '',
displayName: '',
role: '',
loading: false,
error: '',
};
const userSlice = createSlice({
name: 'user',
initialState,
reducers: {},
extraReducers: builder => {
builder.addCase(login.pending, (state, action) => {
state.loading = true;
}),
builder.addCase(login.fulfilled, (state, action) => {
const saveToken = async () => {
await AsyncStorage.setItem('token', action.payload.token);
};
saveToken();
return {
...state,
loading: false,
...action.payload,
};
}),
builder.addCase(login.rejected, (state, action) => {
return {
...state,
loading: false,
error: action.payload,
};
});
},
});
export default userSlice.reducer;
I have to define SavedToken for storing the token and try to get the value from the getData function. if the console.log(inside the getData then i will get the token value but if the console out the function i will get undefined
Maybe you should do it differently.
I don’t know your project structure but I think you could do something like that in your App.js
// get and save token before anything
useEffect(() => {
const getData = async () => {
const token = await AsyncStorage.getItem('token');
// save the token to the redux store
setUser({token});
};
getData();
}, []);
You can read this interesting link Authentication flow and Implement the logic for restoring the token
in react-redux:
API file, returnes data from DB:
import axios from "axios";
const URL = "http://127.0.0.1:8000/GetAllExercises/";
export function GetAllExercies(){
return new Promise((resolve) =>
axios.get(URL).then((res)=> resolve({data: res.data})))
}
Slice File, calling the async function and setting the payload to the varibale- exercises:
import { createAsyncThunk, createSlice, PayloadAction } from "#reduxjs/toolkit";
import { RootState, AppThunk } from "../../app/store";
import { GetAllExercies } from "../API/GetAllExercisesAPI";
export interface CounterState {
exercieses: string[];
status: "idle" | "loading" | "failed";
}
const initialState: CounterState = {
exercieses: [],
status: "idle",
};
export const GetAllExerciesAsync = createAsyncThunk(
"get_all_exercises/GetAllExercies",
async () => {
const response = await GetAllExercies();
return response;
}
);
export const GetAllExerciesSlice = createSlice({
name: "get_all_exercises",
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(GetAllExerciesAsync.pending, (state) => {
state.status = "loading";
})
.addCase(
GetAllExerciesAsync.fulfilled,
(state, action: PayloadAction<any>) => {
state.status = "idle";
console.log(action.payload);
state.exercieses = action.payload;
--> console.log(state.exercieses);
}
);
},
});
export const selectExercises = (state: RootState) => state.get_all_exercises.exercieses;
export default GetAllExerciesSlice.reducer;
at the line with arrow i get this at the console debugger(f12):
{data:Array(17)}
how can i accesses the data inside of it?
If you want access the data duting setting state then you can simply access data from payload
export const GetAllExerciesSlice = createSlice({
name: "get_all_exercises",
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(GetAllExerciesAsync.pending, (state) => {
state.status = "loading";
})
.addCase(
GetAllExerciesAsync.fulfilled,
(state, action: PayloadAction<any>) => {
state.status = "idle";
// data is inside action.payload
state.exercieses = action.payload.data;
console.log(state.exercieses);
}
);
},
});
Apparently you are assigning an object to your "exercieses" variable. If using redux there should be a reducer where you do this.
Something similar to this:
const { response } = action.payload
return {...state, exercises:response.data}
Profile Slice:
import { createSlice, createAsyncThunk } from '#reduxjs/toolkit'
import { IMAGE_API, ACCESS_KEY } from "../../app/utils/constant";
export const getImages = createAsyncThunk('images', async () => {
return fetch(`${IMAGE_API + ACCESS_KEY}`).then((res) =>
res.json()
)
})
console.log(IMAGE_API + ACCESS_KEY);
const ProfilePicSlice = createSlice({
name: 'imageList',
initialState: {
images: [],
loading: false,
},
extraReducers: (builder) => {
builder.addCase(getImages.pending, (state) => {
state.loading = true;
})
builder.addCase(getImages.fulfilled, (state, action) => {
state.loading = false;
state.images.push(action.payload);
console.log(action.payload)
})
builder.addCase(getImages.rejected, (state) => {
state.loading = true;
})
}
});
export default ProfilePicSlice.reducer
Form Slice:
import { createSlice } from "#reduxjs/toolkit";
const initialState = []
const UserSlice = createSlice({
name: 'users',
initialState,
reducers: {
addUser: (state, action) => {
state.push(action.payload);
}
}
});
export const {addUser} = UserSlice.actions;
export default UserSlice.reducer;
I want to add custom param in API URL in asyncThunk '${IMAGE_API + 'custom param' + ACCESS_KEY}
custom param should come from form slice data.
redux-toolkit async thunks are able to access the ThunkAPI, which includes a function to get the current redux state object, it is the second argument passed to the thunk callback. From here you can select the state value you need.
See PayloadCreator.
Example:
export const getImages = createAsyncThunk(
'imageList /images',
(_, { getState }) => {
const store = getState();
const param = state.users./* access into state to get property */;
return fetch(`${IMAGE_API}${param}${ACCESS_KEY}`)
.then((res) => res.json());
}
);
So I'm new to React and Redux Toolkit, I've been trying to get some 'posts' from my localhost API, I do get the payload and it gets displayed in the Redux Dev Tools, nevertheless I can't get this payload to be put on the state.
My postSlice.js :
import { createSlice, createAsyncThunk } from "#reduxjs/toolkit";
import axios from "axios";
export const getPosts = createAsyncThunk("posts", async () => {
try {
const res = await axios({
method: "get",
url: `${process.env.REACT_APP_API_URL}api/post`,
withCredentials: true,
});
console.log(res.data);
return res.data;
} catch (err) {
return err.res.data;
}
});
const postsSlice = createSlice({
name: "posts",
initialState: {
posts: [],
loading: false,
error:"",
},
extrareducers: {
[getPosts.pending]: (state, action) => {
state.loading = true;
},
[getPosts.fulfilled]: (state, action) => {
console.log(action.payload);
state.loading = false;
state.posts = action.payload;
},
[getPosts.rejected]: (state, action) => {
state.loading = false;
state.posts = action.payload.message;
},
},
});
export default postsSlice.reducer;
Then I use the dispatch on a React Component
import { useDispatch } from "react-redux";
import { getPosts } from "../redux/features/postSlice";
const Thread = () => {
const [loadPost, setLoadPost] = useState(true);
const dispatch= useDispatch();
useEffect(() => {
if (loadPost ) {
dispatch(getPosts());
setLoadPost(false)
}
}, [loadPost, dispatch]);
return <div>Fil d'actualités</div>;
};
export default Thread;
Finally I get this on the State in Redux Dev Tool
posts(pin):[]
loading(pin): false
error(pin): ""
And this on the Action:
payload (pin): [{...}{...}{...}]
Also, through the pending, fulfilled and rejected states, loading won't change a bit, even if I pass it on true or false directly with VSCode nor the log I put on the fulfilled extra reducer, it's like the action doesn't affect the state at all, therefore I have the another reducer working fine with the async functions, any help would be much appreciated!
EDIT: My bad, i made a misspelling mistake, 'r' instead of 'R' in 'extraReducers' declaration, the kind of error you feel so dumb about
I think you should use extraRedcuer that way:
extraReducers: (builder) => {
builder.addCase(getPosts.pending, (state, action) => {
state.loading = true;
}),
builder.addCase(getPosts.fulfilled, (state, action) => {
console.log(action.payload);
state.loading = false;
state.posts = action.payload;
}),
builder.addCase(getPosts.rejected, (state, action) => {
state.loading = false;
state.posts = action.payload.message;
}),
},
I am trying to create a scraping application using redux toolkit for learning purposes but the scraping process fails whenever I pass custom parameters in the dispatch statement but works correctly on passing default parameters in the thunk
My async thunk
export const loadData = createAsyncThunk(
"alldata/getdata",
async ({ pageNo, language }, thunkAPI) => {
const data = await fetch(
`http://localhost:5000/scrape?pageNo=${encodeURIComponent(
pageNo
)}&language=${encodeURIComponent(language)}`
);
const json = data.json();
return json;
}
);
My slice
const projectSlice = createSlice({
name: "allprojects",
state: {
projectState: [],
workingState: [],
isLoading: false,
hasError: false,
},
reducers: {
addProject: (state, action) => {
return state.workingState.push(action.payload);
},
removeProject: (state, action) => {
return state.workingState.filter(
(project) => project.id !== action.payload.id
);
},
},
extraReducers: {
[loadData.pending]: (state, action) => {
state.isLoading = true;
state.hasError = false;
},
[loadData.fulfilled]: (state, action) => {
const { json } = action.payload;
state.isLoading = false;
state.hasError = false;
},
[loadData.rejected]: (state, action) => {
state.isLoading = false;
state.hasError = true;
},
},
});
export const { addProject, removeProject } = projectSlice.actions;
export const { Projectreducer } = projectSlice.reducer;
export const selectAllPosts = (state) => state.allprojects.projectState;
Calling the async action
React.useEffect(() => {
console.log(dispatch(loadData(1, "ruby")));
}, []);
//url:https://github.com/search?p=undefined&q=language%3Aundefined
how do I solve this error
The async thunk arguments must be collected in one object:
dispatch(loadData({ pageNo: 1, language: "ruby" }))
See https://redux-toolkit.js.org/api/createAsyncThunk#payloadcreator