I have App contains Tabs that gets data from API bassed on token I passed to the header request,
So in the login screen, i dispatch an action to save token after user login and it's saved well
But the issue is after user login and go to home screen "save token Action dispatched" I got error 401 unauthorized, and when I log Token in getting data function I got empty in the debugger although save token dispatched".
But when I open the app again after killing App and Go to Home " because I'm login before and save token, And I use redux-persist to save it so it's saved before when logging first time" its work fine!
So I don't know whats the wrong When Login at first time!
here's Home Screen Code snippet
constructor(props) {
super(props);
this.state = {
token: this.props.token,
}
}
// Get all playList user created
getAllPlayLists = async () => {
const {token} = this.state;
console.log(token); // After first time I login I got here empty, But after i kill the app or re-open it i got the token well :)
let AuthStr = `Bearer ${token}`;
const headers = {
'Content-Type': 'application/json',
Authorization: AuthStr,
};
let response = await API.get('/my_play_list', {headers: headers});
let {
data: {
data: {data},
},
} = response;
this.setState({playlists: data});
};
componentDidMount() {
this.getAllPlayLists();
}
const mapStateToProps = state => {
console.log('is??', state.token.token); here's i got the token :\\
return {
token: state.token.token,
};
};
export default connect(mapStateToProps)(Home);
Redux stuff
reducers
import {SAVE_TOKEN} from '../actions/types';
let initial_state = {
token: '',
};
const saveTokenReducer = (state = initial_state, action) => {
const {payload, type} = action;
switch (type) {
case SAVE_TOKEN:
state = {
...state,
token: payload,
};
break;
}
return state;
};
export default saveTokenReducer;
--
import {IS_LOGIN} from '../actions/types';
let initialState = {
isLogin: false,
};
const userReducer = (state = initialState, action) => {
switch (action.type) {
case IS_LOGIN:
state = {
...state,
isLogin: true,
};
break;
default:
return state;
}
return state;
};
export default userReducer;
actions
import {SAVE_TOKEN} from './types';
export const saveToken = token => {
return {
type: SAVE_TOKEN,
payload: token,
};
};
-
import {IS_LOGIN} from './types';
export const isLoginFunc = isLogin => {
return {
type: IS_LOGIN,
payload: isLogin,
};
};
store
const persistConfig = {
key: 'root',
storage: AsyncStorage,
};
const rootReducer = combineReducers({
user: userReducer,
count: countPlayReducer,
favorite: isFavoriteReducer,
token: saveTokenReducer,
});
const persistedReducer = persistReducer(persistConfig, rootReducer);
Edit
I figure out the problem, Now in the Login function after getting the response from the API I dispatch two actions Respectively
facebookAuth = async()=>{
....
this.props.dispatch(isLoginFunc(true)); // first
this.props.dispatch(saveToken(token)); // second
....
}
But when I dispatch saveToken(token) firstly I can see the token in the debugger without problems!
So how can i handle it and dispatch two actions at the same time?
When response token get then redirect to page. Maybe should add callback function for action. For example:
This following code is for add record
addVideoGetAction(values, this.props.data, this.callBackFunction)
This callBackFunction is close the modal
callBackFunction = (value: any) => {
this.setCloseModal();
};
You will use callback function in login action. This function will redirect to the page
This function call in saga. this following code
function* setAddOrUpdate(params: any) {
params.callback(redirectPageParams);
}
In redux we should NEVER alter the state object in reducer ... we return a brand new object
const saveTokenReducer = (state = initial_state, action) => {
const {payload, type} = action;
switch (type) {
case SAVE_TOKEN:
state = {
...state,
token: payload,
};
break;
}
return state;
};
Instead
const saveTokenReducer = (state = initial_state, action) => {
const { payload, type } = action;
switch (type) {
case SAVE_TOKEN:
return { ...state, token: payload };
default:
return state;
}
};
Regarding dispatching two actions at the same time
const userReducer = (state = initial_state, { action, type }) => {
switch (type) {
case UPDATE_LOGIN:
return { ...state, token: payload, isLogin: !!payload };
default:
return state;
}
};
-
facebookAuth = async () => {
this.props.dispatch(updateLogin(token));
};
-
import { UPDATE_LOGIN } from './types';
export const updateLogin = token => {
return {
type: UPDATE_LOGIN,
payload: token,
};
};
Related
After introducing Redux to my React Native Expo app, whenever I try to interact with the database my app stops working.
actions.js:
export const SET_SELECTED_PLAYERS = "SET_SELECTED_PLAYERS"
export const SET_PLAYERS = "SET_PLAYERS"
export const SET_SELECTED_COURSE = "SET_SELECTED_COURSE"
export const SET_COURSES = "SET_COURSES"
//Player actions
export const setPlayers = (players) => (
{ type: SET_PLAYERS, payload: players, }
)
export const setSelectedPlayers = (players) => (
({ type: SET_SELECTED_PLAYERS, payload: players, })
)
export const setSelectedCourse = (course) =>
({ type: SET_SELECTED_COURSE, payload: course, })
export const setCourses = (courses) =>
({ type: SET_COURSES, payload: courses, })
reducers.js:
import { SET_PLAYERS, SET_SELECTED_PLAYERS, SET_SELECTED_COURSE, SET_COURSES } from "./actions"
const initialState = {
players: [],
selectedPlayers: [],
courses: [],
selectedCourse: null,
round: {}
}
export const playerReducer = (state = initialState, action) => {
switch (action.type) {
case SET_PLAYERS:
return { ...state, players: action.payload }
case SET_SELECTED_PLAYERS:
return { ...state, selectedPlayers: action.payload }
default:
return state
}
}
export const courseReducer = (state = initialState, action) => {
switch (action.type) {
case SET_SELECTED_COURSE:
return { ...state, selectedCourse: action.payload }
case SET_COURSES:
return { ...state, courses: action.payload }
default:
return state
}
}
store.js:
import { createStore, combineReducers, applyMiddleware } from "redux";
import { courseReducer, playerReducer } from "./reducers";
const rootReducer = combineReducers({ playerReducer, courseReducer })
export const Store = createStore(rootReducer)
SQLite used in component :
const dispatch = useDispatch()
const db = SQLite.openDatabase("players.db")
useEffect(() => {
db.transaction(tx => {
tx.executeSql("SELECT * FROM Player", [], (trans, result) => {
dispatch(setPlayers(result.rows._array))
})
})
}, [])
Table for Player exists and app worked before I introduced Redux. It interacts with Firebase and when fetching data from cloud Redux has no problems. What problems could it have with SQLite?
Use sqlite query in redux action
export const getUsers = () => {
try {
return async dispatch => {
const result = await fetch('https://jsonplaceholder.typicode.com/users', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
});
const json = await result.json();
if (json) {
dispatch({
type: GET_USERS,
payload: json
})
} else {
console.log('fetch user api error');
}
}
} catch (error) {
console.log('action error');
}
}
userSlice
import { createSlice, createAsyncThunk } from "#reduxjs/toolkit";
import LoginService from "../../Services/Login.service";
export const userRegister = createAsyncThunk(
"users/register",
async (params) => {
try {
const { registerForm } = params;
const { data } = await LoginService.register(registerForm);
return data;
} catch (error) {
}
}
);
const initialState = {
userData: {},
errorResponse: null,
status: "idle",
};
export const userSlice = createSlice({
name: "User",
initialState,
reducers: {},
extraReducers: {
[userRegister.pending]: (state, action) => {
state.status = "loading";
},
[userRegister.fulfilled]: (state, action) => {
state.status = "succeeded";
state.userData = action.payload;
},
[userRegister.error]: (state, action) => {
state.status = "failed";
state.errorResponse = action.payload;
},
},
});
export default userSlice.reducer;
Login.service.js
import axios from "axios";
const API = axios.create({ baseURL: 'http://localhost:3001'});
const LoginService = {
register: async (registerData ) => {
await API.post('/users/register', registerData)
}
};
export default LoginService;
Hi.I try add register feature to my app. But when i submit register form, the datas is saved to the database without any problems. But this line const data = await LoginService.register(registerForm); doesnt work data is undefined but when i same post request in postman i get response data the way i want.
LoginService.register is not returning anything,
you can fix that by doing:
const LoginService = {
register: async (registerData ) => {
const response = await API.post('/users/register', registerData);
return response.data;
}
};
I wanna access an array called "currentNode" from the react context, but looks like it does not work. The console error message is the following:
Unhandled Rejection (TypeError): Cannot read property '0' of null.
It seems that I cannot access the currentNode variable from the context.
Can you identify the mistake? Help is very much appreciated.
Step 1: Here is the code, how I dispatch it to the context:
const AuthState = (props) => {
// auth state: create initial authState
const initialState = {
isAuthenticated: false,
chatId: null,
currentNode: [],
};
const fetchCurrentNode = async (chatId) => {
try {
const config = {
headers: {
"x-auth-token": "",
},
};
const res1 = await axios.get(
`http://localhost:4001/api/chatbots/${chatId}/nodeid`,
//`https://mybot.uber.space/api/chatbots/${chatid}`
//`https://sandbox.as.wiwi.uni-goettingen.de/teachr/chatbots/pin/${pin}`
config
);
dispatch({
type: NEW_CURRENT_NODE_CREATED,
payload: currentNode,
});
} catch (error) {
console.error(error);
}
};
return (
<ChatContext.Provider
value={{
chatId: state.chatId,
chat: state.chat,
chatSessionId: state.chatSessionId,
createChatSessionId,
addBackendAnswerToChat,
addUserInputToChat,
resetChat,
fetchEventAnswerFromDialogflow,
fetchAnswerFromDialogflow,
}}
>
{props.children}
</ChatContext.Provider>
);
};
Step 1b: Here is my context declaration:
import React from 'react';
const chatContext = React.createContext();
export default chatContext;
Step 2: Here is how I saved it as Types:
export const NEW_CURRENT_NODE_CREATED = "NEW_CURRENT_NODE_CREATED";
Step 3: Here is my reducer function:
import { CHATID_FETCH_SUCCESSFUL, NEW_CURRENT_NODE_CREATED } from "./authTypes";
export default (state, action) => {
switch (action.type) {
case NEW_CURRENT_NODE_CREATED:
return {
...state,
isAuthenticated: true,
currentNode: [action.payload],
};
default:
return state;
}
};
Step 4: And here is how I wanna get access to the currentNode variable:
import AuthContext from "../../Context/Auth/authContext";
const authContext = useContext(AuthContext);
const { chatId, currentNode } = authContext;
console.log(chatId);
console.log(currentNode);
You didn't add value you are trying to use to the context value
return (
<ChatContext.Provider
value={{
chatId: state.chatId,
chat: state.chat,
currentNode: state.currentNode, // <-- ADD THIS LINE
chatSessionId: state.chatSessionId,
createChatSessionId,
addBackendAnswerToChat,
addUserInputToChat,
resetChat,
fetchEventAnswerFromDialogflow,
fetchAnswerFromDialogflow,
}}
>
{props.children}
</ChatContext.Provider>
);
};
I am like in a strange problem. The problem is that I am trying to make an API hit (in service file) which in turn provides some data (it is working), this data is to be updated in my reducer1.js and then returned. Now, my issue is though the value is coming in reducer file, but is not returned, so in turn, state is not changed, and in turn my end component is not rerendered.
Now, when my service file is successfully hitting and then returning data to my reducer1.js, why in the world the updated-state is not returned by "GET_List" action type? Can someone see any problem?
index.js (service file)
const global = {
getActressList: async function(){
const response = await fetch("http://localhost:2000/api/actressList");
const data = await response.json();
return data;
}
}
export default global;
reducer1.js
import global from '../../services/index';
const initialState = {
data: [
{
id: 1,
name: "Aishwarya Rai",
src: "/assets/img/aishwarya.png"
}
]
};
function reducer1(state = initialState, action) {
switch (action.type) {
case "GET_LIST": {
const data = global.getActressList();
data.then((res)=> {
return {
...state,
data: res
}
})
}
default:
return state;
}
}
export default reducer1;
Result:
You are returning from a promise not from a reducer function:
function reducer1(state = initialState, action) {
switch (action.type) {
case "GET_LIST": {
const data = global.getActressList();
data.then((res) => {
// here you are returning from a promise not from a reducer function
return {
...state,
data: res,
};
});
}
default:
return state;
}
}
The code in reducer should be sync like this:
function reducer1(state = initialState, action) {
switch (action.type) {
case "GET_LIST": {
return {
...state,
data: action.payload,
};
}
default:
return state;
}
}
And your data fetching should be moved to component effect like this:
function YourComponent() {
const dispatch = useDispatch();
const data = useSelector(state => state.data)
useEffect(() => {
const data = global.getActressList();
data.then((res) => {
dispatch({type: 'GET_LIST', payload: res});
});
}, [])
...
}
EDIT
If you use class components the fetching logic should be placed in componentDidMount lifecycle hook like this:
class YourComponent extends Component {
state = { data: [] };
componentDidMount() {
const data = global.getActressList();
data.then((res) => {
dispatchYourAction({type: 'GET_LIST', payload: res});
});
}
...
}
In the last couple of days I have been working on my Redux api call. I am actually having a problem getting the data back to the view component. Currently I'm able to see the data in the in the action generator, so I know at least I'm able to get it. However, nothing is showing in the view. I imagine it may have something to do with when it's loading. This is why I tried to load it when the component is rendering.
https://djangoandreact.herokuapp.com/user/1 is what is not loading.
codesandbox: https://codesandbox.io/s/zlor60q3jm?from-embed
Should be able to go to /user/1 at the end similar to going to /1 brings up an article(Tough Hope)
Heres the view component:
import React from "react";
import { connect } from "react-redux";
import { fetchUser } from "../store/actions/userActions";
class UserDetailView extends React.Component {
componentDidMount() {
const userID = this.props.match.params.userID;
fetchUser(userID); //fixed
}
render() {
const { user } = this.props.user;
console.log(user);
return (
<div>
<h3>{user.username}</h3>
</div>
);
}
}
const mapStateToProps = state => ({
user: state.user
});
const mapDispatchToProps = (dispatch, ownProps) => ({
fetchUser: dispatch(fetchUser(ownProps.match.params.userID))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(UserDetailView);
Action generator
import axios from "axios";
import { thunk } from "react-redux";
export function fetchUser(userID) {
console.log(userID);
return dispatch => {
return axios.get(`/api/user/${userID}`).then(res => {
dispatch(fetchUserSuccess(res.data));
console.log(res.data); // loads data
});
};
}
// Handle HTTP errors since fetch won't.
function handleErrors(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}
export const FETCH_USER_BEGIN = "FETCH_USER_BEGIN";
export const FETCH_USER_SUCCESS = "FETCH_USER_SUCCESS";
export const FETCH_USER_FAILURE = "FETCH_USER_FAILURE";
export const fetchUserBegin = () => ({
type: FETCH_USER_BEGIN
});
export const fetchUserSuccess = user => ({
type: FETCH_USER_SUCCESS,
payload: { user }
});
export const fetchUserFailure = error => ({
type: FETCH_USER_FAILURE,
payload: { error }
});
Reducers(which are probably fine):
import {
FETCH_USER_BEGIN,
FETCH_USER_SUCCESS,
FETCH_USER_FAILURE
} from "../actions/actionTypes";
const initialState = {
user: {},
loading: false,
error: null
};
export default function userReducer(state = initialState, action) {
switch (action.type) {
case FETCH_USER_BEGIN:
return {
...state,
loading: true,
error: null
};
case FETCH_USER_SUCCESS:
return {
...state,
loading: false,
user: action.payload.user
};
case FETCH_USER_FAILURE:
return {
...state,
loading: false,
error: action.payload.error,
user: {}
};
default:
return state;
}
}
folks. I found it.
case FETCH_USER_SUCCESS:
return {
...state,
loading: false,
user: action.payload.user
};
user is supposed to be user:action.payload
Also, the user action was supposed to be
export const fetchUserSuccess = user => ({
type: FETCH_USER_SUCCESS,
payload: user
})
WOOOOW. But, honestly, I learned so much about Redux in the last two sleepless nights, it was worth the pain. Really was. Now, instead of copy pasta, I know what an action generator is and does, and reducer (obvi)