I have an App where I use Ads, and if the user has Premium access, those ads should be off when launching the app. For that I used Redux. I use Firebase auth, and the user object contains an entry called "hasPremium". Now when redux loads on the root (App.js), the auth object is null and the premium is always false, after like 2-3 secs the auth becomes not null, but by that time the initialState is false.
Any smart way to wait for the auth, and only after that create the InitialState for Redux?
App.js
import { Provider } from "react-redux";
import thunk from "redux-thunk";
import { createStore, applyMiddleware } from "redux";
var middlewares = applyMiddleware(thunk);
const store = createStore(reducers, middlewares)
export default function App() {
return (
<>
<Provider store={store}>
<>{isReady ? <AuthNavigation/> : <LoadingScreen/>}</>
</Provider>
</>
);
}
admob.reducer.js
const AD_ON = "AD_ON";
const AD_OFF = "AD_OFF";
async function checkPremium(){
let hasPremium = await checkPremiumForUserAsync()
return hasPremium
}
const initialState = {
ad_status: checkPremium() ?? false, // change this depending on hasPremium from Firebase auth
};
export default (ad_status = initialState, { type }) => {
switch (type) {
case AD_ON:
return { ad_status: true };
case AD_OFF:
return { ad_status: false };
default:
return ad_status;
}
};
admob.action.js
export const ToggleAdOn = () => ({
type: AD_ON,
});
export const ToggleAdOff = () => ({
type: AD_OFF,
});
export const ToggleAds = (ads_state) => {
return async (dispatch) => {
if (ads_state === true) {
dispatch(ToggleAdOn());
} else {
dispatch(ToggleAdOff());
}
};
};
Related
I'm building a site with basic auth with Spring. I use Redux. I'm sending a request to "/auth" in the backend. After successfully logging in, i get those:
enter image description here
As you can see I have successfully logged in.
But i still can not be authenticated. I did not refresh the page i did nothing but this is the console output. By the way, postman is working fine.
enter image description here
This is part of apiCalls:
import axios from "axios";
export const signup = (body) => {
return axios.post('/users', body);
};
export const login = creds => {
return axios.post('/auth', {}, {auth:creds});
};
export const getMarketItemsSortByDate = () => {
return axios.get("/market/last");
}
This is configureStore:
import {createStore, applyMiddleware,compose} from 'redux';
import authReducer from './authReducer';
import SecureLS from 'secure-ls';
import thunk from 'redux-thunk';
const secureLS = new SecureLS();
const getStateFromStorage = () => {
const hoaxAuth = secureLS.get('hoax-auth');
let stateInLocalStorage = {
isLoggedIn:false,
username:undefined,
mail:undefined,
balance:undefined,
password:undefined
};
if(hoaxAuth){
stateInLocalStorage = hoaxAuth;
}
return stateInLocalStorage;
}
const updateStateInStorage = newState => {
secureLS.set('hoax-auth', newState);
}
const configureStore = () => {
const initialState = getStateFromStorage();
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(authReducer, initialState, composeEnhancers(applyMiddleware(thunk)));
store.subscribe(()=> {
updateStateInStorage(store.getState());
})
return store;
}
export default configureStore;
This is authActions :
import * as ACTIONS from "./Constants";
import {login} from '../api/apiCalls';
export const logoutSuccess = () => {
return {
type: ACTIONS.LOGOUT_SUCCESS
};
}
export const loginSuccess = authState => {
return {
type: ACTIONS.LOGIN_SUCCESS,
payload: authState
};
};
export const loginHandler = credentials => {
return async function(dispatch) {
const response = await login(credentials);
const authState = {
...response.data,
password: credentials.password,
};
console.log(authState);
dispatch(loginSuccess(authState));
return response;
};
};
And this is authReducer:
import * as ACTIONS from './Constants';
const defaultState = {
isLoggedIn:false,
username:undefined,
mail:undefined,
balance:undefined,
password:undefined
}
const authReducer = (state= { ...defaultState},action) => {
if(action.type === ACTIONS.LOGOUT_SUCCESS){
return defaultState;
} else if(action.type === ACTIONS.LOGIN_SUCCESS){
return {
...action.payload,
isLoggedIn:true
}
}
return state;
}
export default authReducer;
Everything is fine with postman so problem should be inside React.js
Make sure to set withCredentials for each requests you made with axios that requires the token as such:
export const login = creds => {
return axios.post('/auth', {}, {auth:creds, withCredentials:true});
};
export const getMarketItemsSortByDate = () => {
return axios.get("/market/last", {}, {withCredentials:true});
}
which allows the access token to be set and to be sent along with the request.
I'm new to React, Redux and have been following tutorials on the topic. I'm come across a lot of issues that I've been able to resolve but I've come across an issue I can't resolve. I set up store, and can even view it through Chrome's Redux Tools and it show correctly, however when I try and dispatch to the Store, I always get a Cannot read property 'dispatch' of undefined error. I have followed numerous tutorials letter for letter, and am still stuck with the same error message.
Index.Js
import Layout from '../components/layout/Layout';
import Home from '../components/Home';
import { getRooms } from '../redux/actions/roomActions';
import { wrapper } from '../redux/store';
export default function Index() {
return (
<Layout>
<Home />
</Layout>
);
}
export const getServerSideProps = wrapper.getServerSideProps(
async ({ req, store }) => {
await store.dispatch(getRooms(req));
}
);
roomConstants.js
export const ALL_ROOMS_SUCCESS = 'ALL_ROOMS_SUCCESS';
export const ALL_ROOMS_FAIL = 'ALL_ROOMS_FAIL';
export const CLEAR_ERRORS = 'CLEAR_ERRORS';
reducer.js
import { combineReducers } from 'redux';
import { allRoomsReducer } from './roomReducers';
const reducer = combineReducers({
allRooms: allRoomsReducer,
});
export default reducer;
Store.js
import { createStore, applyMiddleware } from 'redux';
import { HYDRATE, createWrapper } from 'next-redux-wrapper';
import thunkMiddleware from 'redux-thunk';
import reducers from './reducers/reducers';
const bindMiddleware = (middleware) => {
if (process.env.NODE_ENV !== 'production') {
const { composeWithDevTools } = require('redux-devtools-extension');
return composeWithDevTools(applyMiddleware(...middleware));
}
return applyMiddleware(...middleware);
};
const reducer = (state, action) => {
if (action.type === HYDRATE) {
const nextState = {
...state,
...action.payload,
};
return nextState;
} else {
return reducers(state, action);
}
};
const initStore = () => {
return createStore(reducer, bindMiddleware([thunkMiddleware]));
};
export const wrapper = createWrapper(initStore);
roomReducer.js
import {
ALL_ROOMS_SUCCESS,
ALL_ROOMS_FAIL,
CLEAR_ERRORS,
} from '../constants/roomConstants';
// All rooms reducer
export const allRoomsReducer = (state = { rooms: [] }, action) => {
switch (action.type) {
case ALL_ROOMS_SUCCESS:
return {
roomsCount: action.payload.roomsCount,
resPerPage: action.payload.resPerPage,
filteredRoomsCount: action.payload.filteredRoomsCount,
rooms: action.payload.rooms,
};
case ALL_ROOMS_FAIL:
return {
error: action.payload,
};
case CLEAR_ERRORS:
return {
...state,
error: null,
};
default:
return state;
}
};
roomAcion.js
import axios from 'axios';
import absoluteUrl from 'next-absolute-url';
import {
ALL_ROOMS_SUCCESS,
ALL_ROOMS_FAIL,
CLEAR_ERRORS,
} from '../constants/roomConstants';
//Clear errors
export const clearErrors = () => async (dispatch) => {
return dispatch({
type: CLEAR_ERRORS,
});
};
// Get all rooms
export const getRooms = (req) => async (dispatch) => {
try {
const { origin } = absoluteUrl(req);
const { data } = await axios.get(`${origin}/api/rooms`);
dispatch({
type: ALL_ROOMS_SUCCESS,
payload: data,
});
} catch (error) {
dispatch({
type: ALL_ROOMS_FAIL,
payload: error.response.data.message,
});
}
};
in index.js, your getServerSideProps function should read this;
export const getServerSideProps = wrapper.getServerSideProps((store) => async ({ req }) => {
await store.dispatch(getRooms(req));})
Use the old version of next-redux-wrapper like 6.0.2
Hi I'm new at using redux and I can't really figure out why the FetchUsers() doesn't work in dispatching and the users I fetch in App.js is always an empty array.Should I use useDispatch()? or is it adding a another middleware?
reducer
import {
FETCH_USERS,
FETCH_USERS_SUCCESS,
FETCH_USERS_FAILURE,
} from "../actions/types";
const initialValues = {
loading: false,
users: [],
error: "",
};
const usersreducer = (state = initialValues, action) => {
switch (action.type) {
case FETCH_USERS:
return { ...state, loading: true };
case FETCH_USERS_SUCCESS:
return { ...state, loading: false, users: action.payload };
case FETCH_USERS_FAILURE:
return { ...state, loading: false, error: action.payload };
default:
return state;
}
};
actions:
import { FETCH_USERS, FETCH_USERS_FAILURE, FETCH_USERS_SUCCESS } from "./types";
const fetchUsersRequest = () => {
return {
type: FETCH_USERS,
};
};
const fetchUsersFailure = (error) => {
return {
type: FETCH_USERS_FAILURE,
payload: error,
};
};
const fetchUsersSuccess = (users) => {
return {
type: FETCH_USERS_SUCCESS,
payload: users,
};
};
export const FetchUsers = () => {
return (dispatch) => {
dispatch(fetchUsersRequest());
fetch("https://jsonplaceholder.typicode.com/users")
.then((res) => {
const users = res.data;
dispatch(fetchUsersSuccess(users));
})
.then((err) => {
const error = "error";
dispatch(fetchUsersFailure(error));
});
};
};
index:
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import thunk from "redux-thunk";
import rootReducer from "./reducers";
const store = createStore(rootReducer, applyMiddleware(thunk));
ReactDOM.render(
<Provider store={store}>
<React.StrictMode>
<App />
</React.StrictMode>
</Provider>,
document.getElementById("root")
);
App.js:
import { FetchUsers } from "./actions/actions";
import { useSelector, useDispatch } from "react-redux";
const App = () => {
const users = useSelector((state) => state.users);
useEffect(() => {
FetchUsers();
console.log(users.users);
}, []);
........................................................................................................
Simply calling a thunk will not cause anything to happen; it will just return another function and do nothing. You need to dispatch it the same way you would dispatch normal actions. Dispatching the thunk will actually run it, and will also allow it to access your store so that you can dispatch other actions and access the current state of your store if you want to. FetchUsers() really is just a regular function; by itself, it doesn't have any way to access your store. Dispatching it provides it with that access.
import { FetchUsers } from "./actions/actions";
import { useSelector, useDispatch } from "react-redux";
const App = () => {
const users = useSelector((state) => state.users);
const dispatch = useDispatch();
useEffect(() => {
dispatch(FetchUsers());
console.log(users.users);
}, []);
See the redux-thunk docs for more information and examples.
Im running into a problem with my redux to firebase connection i believe.
Trying to grab all jobs from users in firebase.
Have my store setup, action and reducer, not really sure where i am going wrong here so i must be overlooking something, nothing is showing up in console and i put a console.log call on my action and nothing shows still.
my action :
// Grab all Jobs
export const getJobs = (jobs) => ({
type: 'GET_JOBS',
jobs
});
export const startGetJobs = () => {
return(dispatch, getState) => {
const uid = getState().auth.uid;
return database.ref(`users/${uid}/jobs`)
.once('value')
.then((snapshot) => {
const jobs =[];
console.log(jobs);
//Parse the data using snapshot
snapshot.forEach((childSnapshot) => {
jobs.push({
id: childSnapshot.key,
...childSnapshot.val()
});
});
dispatch(getJobs(jobs));
});
};
};
my reducer file :
const jobReducerDefaultState = [];
export default (state= jobReducerDefaultState, action) => {
switch(action.type) {
case 'ADD_JOB':
return [
...state,
action.job
];
case 'REMOVE_JOB':
return state.filter(({ id }) => id !== action.id);
case 'EDIT_JOB':
return state.map((job) => {
if(job.id === action.id) {
return {
...job,
...action.updates
};
} else {
return job;
}
});
case 'GET_JOBS':
return action.jobs;
default:
return state;
}
};
my redux store file :
import { createStore, combineReducers, applyMiddleware, compose} from 'redux';
import thunk from 'redux-thunk';
import jobsReducer from '../reducers/jobs';
import filtersReducer from '../reducers/filters';
import authReducer from '../reducers/auth';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
export default () => {
const store = createStore(
combineReducers({
jobs: jobsReducer,
filters: filtersReducer,
auth: authReducer
}),
composeEnhancers(applyMiddleware(thunk))
);
return store;
};
And trying to call that with this component :
import React from 'react';
import { connect } from 'react-redux';
import JobDataItem from './JobDataItem';
import { startGetJobs } from '../actions/jobs';
class JobData extends React.Component {
ComponentDidMount() {
this.props.startGetJobs();
}
render() {
return (
<div>
{this.props.jobs.map((job) => {
return <JobDataItem key={job.id} company={job.company}/>
})}
</div>
);
};
};
const mapDispatchToProps = (dispatch) => {
return {
startGetJobs: (jobs) => dispatch(startGetJobs(jobs))
}
}
export default connect(undefined, mapDispatchToProps)(JobData);
which passes that data to the jobDataItem component to render to screen below:
import React from 'react';
import { Link } from 'react-router-dom';
const JobDataItem = ({ id, company}) => (
<div>
<Link to={`/edit/${id}`}>
<h3>{company}</h3>
</Link>
</div>
);
export default JobDataItem;
my firebase db formats like:
users/
user-uid/
jobs/
job-uid/
company:"Company Name",
jobTitle:"jobTitle:,
And so on...
Expected output is "Company Name" but nothing shows up at all. i try to just call props.jobs.length and it shows up as 0 as well.
EDITED
1. “Called startGetJobs in componentDidMount(),
2. “Changed props.jobs.map((job)... to this.props.jobs.map((job)...
I now get props is undefined error in console and nothing still appears on screen.
Currently I am trying to pass user data through my react app with Redux. I have created a user API with a django backend that is definately working, as I am able to go the url and see all the json that comes out of it. However, when I try to pass it into a component I keep getting undefined. Here is my code:
userActions.js:
import Axios from "axios";
export function getUser() {
const id = this.params.match.id
return dispatch => {
dispatch(fetchUserBegin());
return Axios.get(`/api/user/${id}`)
.then((res) => {
this.setState({
user: res.data,
})
})
}
}
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 }
});
userReducer.js
import { FETCH_USER_BEGIN, FETCH_USER_SUCCESS, FETCH_USER_FAILURE } from '../actions/actionTypes'
const initialState = {
user: {},
loading: false,
error: null
};
export default function productReducer(state = initialState, action) {
switch(action.type) {
case FETCH_USER_BEGIN:
// Mark the state as "loading" so we can show a spinner or something
// Also, reset any errors. We're starting fresh.
return {
...state,
loading: true,
error: null
};
case FETCH_USER_SUCCESS:
// All done: set loading "false".
// Also, replace the items with the ones from the server
return {
...state,
loading: false,
user: action.user
};
case FETCH_USER_FAILURE:
// The request failed, but it did stop, so set loading to "false".
// Save the error, and we can display it somewhere
// Since it failed, we don't have items to display anymore, so set it empty.
// This is up to you and your app though: maybe you want to keep the items
// around! Do whatever seems right.
return {
...state,
loading: false,
error: action.payload.error,
user: {}
};
default:
// ALWAYS have a default case in a reducer
return state;
}
}
And the display component:
UserInformation.js:
import React from "react";
import { connect } from "react-redux";
import { getUser } from "../store/actions/userActions";
class UserDetailView extends React.Component {
componentDidMount() {
this.props.dispatch(getUser());
}
render() {
const { user } = this.props;
console.log(user)
return (
<ul>
{user.map(user =>
<li key={user.id}>{user.username}</li>
)}
</ul>
);
}
}
const mapStateToProps = state => ({
user: state.user,
});
export default connect(mapStateToProps)(UserDetailView);
Index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { createStore, compose, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import reducer from './store/reducers/auth';
const composeEnhances = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
const store = createStore(reducer, composeEnhances(
applyMiddleware(thunk)
))
const app = (
<Provider store={store}>
<App />
</Provider>
)
ReactDOM.render(app, document.getElementById('root'));
registerServiceWorker();
Anyone got any ideas why this isn't working?
You're not supposed to setState() in that action creator:
this.setState({
user: res.data,
})
you should dispatch an action instead
Try this:
export function getUser() {
const id = this.params.match.id
return dispatch => {
dispatch(fetchUserBegin());
return Axios.get(`/api/user/${id}`)
.then( res => {
dispatch(fetchUserSuccess(res.data);
})
}
}
You should pass the mapDispatchToProps function to the connect() method as the second argument, like this:
import React from "react";
import { connect } from "react-redux";
import { getUser } from "../store/actions/userActions";
class UserDetailView extends React.Component {
componentDidMount() {
this.props.getUser() //fixed
}
render() {
const { user } = this.props;
console.log(user)
return (
<ul>
{user.map(user =>
<li key={user.id}>{user.username}</li>
)}
</ul>
);
}
}
const mapStateToProps = state => ({
user: state.user,
});
const mapDispatchToProps = dispatch => ({ //added
getUser: dispatch(getUser())
})
export default connect(mapStateToProps,mapDispatchToProps)(UserDetailView); //fixed
And also fix this:
case FETCH_USER_SUCCESS:
// All done: set loading "false".
// Also, replace the items with the ones from the server
return {
...state,
loading: false,
user: action.payload.user //fixed
};