redux: action is fired but not changing state - javascript

I have a problem with my code.
when I dispatch an action Of type "LOGIN"
The next component is Called with this HOC
withHOC
import React from "react";
import { connect } from "react-redux";
export default function withState(WrappedComponent) {
async function mapStateToProps(reduxState) {
const state = await reduxState.initialReducer;
return {
...state ,
};
}
return connect(
mapStateToProps,
null
)(function (props) {
return (
<React.Fragment>
<WrappedComponent {...props} />
</React.Fragment>
);
});
}
DispatchFile.js
const SignIn = props => {
const classes = useStyles();
function handleClick(){
const email = document.getElementById("email").value;
const password = document.getElementById("password").value;
props.dispatch({
type: LOGIN,
cred: {email, password}
})
}
return(
<div className={classes.inputGroup}>
<TextField type="email" className={classes.btnColor} id="email" label="E-mail" variant="filled" />
<TextField type="password" className={classes.btnColor} id="password" label="Password" variant="filled" />
<Button className={classes.submit} onClick={handleClick}> Login </Button>
</div>
)
}
the action is dispatched successfully to my rootReducer
Root Reducer File
import { combineReducers} from 'redux';
import initialReducer from "./initialReducer";
const rootReducer = combineReducers({
initialReducer,
});
export default rootReducer;
initialReducer
import { CREATE_NEW_USER, LOGIN, REMOVE_USER } from "../actions";
import API from "../actions/API";
let initialState = {
id:"",
username:"",
profileImageUrl:"",
token:"",
isLogged:false,
}
export default async function initialReducer(state = initialState, action){
let newState = {...state};
switch(action.type){
case LOGIN:
const res = await API.login(action.cred);
if( res.code === 200){
console.log(res)
if(action.cred.keepMeLogged)await localStorage.setItem("user", res.res)
const { id, username, profileImageUrl, token} = res.res;
return {
...newState,
id,
username,
profileImageUrl,
token,
isLogged: true,
}
}
else{
return res
}
default:
if(localStorage.getItem("user")){
initialState = localStorage.getItem("user");
}
return newState;
}
}
The action is called successfully and the API returns the correct values
now when I check my redux dev tool the state is set with an empty state {}
what I tried:
searched for similar questions and tried fiddling with code but still, the redux state won't change

Related

Log-in page not redirecting to user dashboard after firebase log-in

I'm trying to make protected routes for my website using React and firebase. It works fine when users log-in with email but when users log-in with firebase, the page refreshes before it has time to update verification status so it reloads to the log-in page again but when I manually refresh, it goes to the protected page. I'm not sure how to fix so that it will work for firebase log-in as well.
Actions.js:
import * as types from "./actionTypes";
import { auth, googleAuthProvider, facebookAuthProvider } from '../Firebase';
const registerStart = () => ({
type: types.REGISTER_START,
});
const registerSuccess = (user) => ({
type: types.REGISTER_SUCCESS,
payload: user,
});
const registerFail = (error) => ({
type: types.REGISTER_FAIL,
payload: error,
});
const loginStart = () => ({
type: types.LOGIN_START,
});
const loginSuccess = (user) => ({
type: types.LOGIN_SUCCESS,
payload: user,
});
const loginFail = (error) => ({
type: types.LOGIN_FAIL,
payload: error,
});
const logoutStart = () => ({
type: types.LOGOUT_START,
});
const logoutSuccess = (user) => ({
type: types.LOGOUT_SUCCESS,
});
const logoutFail = (error) => ({
type: types.LOGOUT_FAIL,
payload: error,
});
export const setUser = (user) => ({
type: types.SET_USER,
payload: user,
})
const googleSignInStart = () => ({
type: types.GOOGLE_SIGN_IN_START,
});
const googleSignInSuccess = (user) => ({
type: types.GOOGLE_SIGN_IN_SUCCESS,
});
const googleSignInFail = (error) => ({
type: types.GOOGLE_SIGN_IN_FAIL,
payload: error,
});
const fbSignInStart = () => ({
type: types.FACEBOOK_SIGN_IN_START,
});
const fbSignInSuccess = (user) => ({
type: types.FACEBOOK_SIGN_IN_SUCCESS,
});
const fbSignInFail = (error) => ({
type: types.FACEBOOK_SIGN_IN_FAIL,
payload: error,
});
export const registerInitiate = (email, password, displayName) => {
return function (dispatch) {
dispatch(registerStart());
auth.createUserWithEmailAndPassword(email, password).then(({user}) => {
user.updateProfile({
displayName
})
dispatch(registerSuccess(user));
}).catch((error) => dispatch(registerFail(error.message)))
}
};
export const loginInitiate = (email, password) => {
return function (dispatch) {
dispatch(loginStart());
auth.signInWithEmailAndPassword(email, password).then(({user}) => {
dispatch(loginSuccess(user));
}).catch((error) => dispatch(loginFail(error.message)))
}
};
export const logoutInitiate = () => {
return function (dispatch) {
dispatch(logoutStart());
auth.signOut().then((resp) =>
dispatch(logoutSuccess())
).catch((error) => dispatch(logoutFail(error.message)));
}
};
export const googleSignInInitiate = () => {
return function (dispatch) {
dispatch(googleSignInStart());
auth.signInWithPopup(googleAuthProvider).then(({user}) => {
dispatch(googleSignInSuccess(user));
}).catch((error) => dispatch(googleSignInFail(error.message)));
}
};
export const fbSignInInitiate = () => {
return function (dispatch) {
dispatch(fbSignInStart());
auth.signInWithPopup(facebookAuthProvider.addScope("user_birthday, email")).then(({user}) => {
dispatch(fbSignInSuccess(user));
}).catch((error) => dispatch(fbSignInFail(error.message)));
}
};
actionTypes.js:
export const REGISTER_START = "REGISTER_START";
export const REGISTER_SUCCESS = "REGISTER_SUCCESS";
export const REGISTER_FAIL = "REGISTER_FAIL";
export const LOGIN_START = "LOGIN_START";
export const LOGIN_SUCCESS = "LOGIN_SUCCESS";
export const LOGIN_FAIL = "LOGIN_FAIL";
export const LOGOUT_START = "LOGOUT_START";
export const LOGOUT_SUCCESS = "LOGOUT_SUCCESS";
export const LOGOUT_FAIL = "LOGOUT_FAIL";
export const SET_USER = "SET_USER";
export const GOOGLE_SIGN_IN_START = "GOOGLE_SIGN_IN_START";
export const GOOGLE_SIGN_IN_SUCCESS = "GOOGLE_SIGN_IN_SUCCESS";
export const GOOGLE_SIGN_IN_FAIL = "GOOGLE_SIGN_IN_FAIL";
export const FACEBOOK_SIGN_IN_START = "FACEBOOK_SIGN_IN_START";
export const FACEBOOK_SIGN_IN_SUCCESS = "FACEBOOK_SIGN_IN_SUCCESS";
export const FACEBOOK_SIGN_IN_FAIL = "FACEBOOK_SIGN_IN_FAIL";
reducer.js:
const initialState = {
loading: false,
currentUser: null,
error: null,
}
const userReducer = (state = initialState, action) => {
switch(action.type) {
case types.REGISTER_START:
case types.LOGIN_START:
case types.LOGOUT_START:
case types.GOOGLE_SIGN_IN_START:
case types.FACEBOOK_SIGN_IN_START:
return {
...state,
loading: true
};
case types.LOGOUT_SUCCESS:
return {
...state,
currentUser: null,
}
case types.SET_USER:
return {
...state,
loading: false,
currentUser: action.payload,
}
case types.REGISTER_SUCCESS:
case types.LOGIN_SUCCESS:
case types.GOOGLE_SIGN_IN_SUCCESS:
case types.FACEBOOK_SIGN_IN_SUCCESS:
return {
...state,
loading: false,
currentUser: action.payload,
};
case types.REGISTER_FAIL:
case types.LOGIN_FAIL:
case types.LOGOUT_FAIL:
case types.GOOGLE_SIGN_IN_FAIL:
case types.FACEBOOK_SIGN_IN_FAIL:
return {
...state,
loading: false,
error: action.payload,
}
default:
return state;
}
}
export default userReducer;
rootReducer.js:
import userReducer from "./reducer";
const rootReducer = combineReducers({
user: userReducer
})
export default rootReducer;
store.js:
import { createStore, applyMiddleware } from "redux";
import { createLogger } from "redux-logger";
import thunk from "redux-thunk";
import rootReducer from './rootReducer';
const middleware = [thunk];
const logger = createLogger({});
if(process.env.NODE_ENV === "development") {
middleware.push(logger)
}
export const store = createStore(rootReducer, applyMiddleware(...middleware));
login.js:
import React, { useState, useEffect } from 'react';
import { useDispatch, useSelector} from "react-redux";
import { useNavigate, Link } from "react-router-dom";
import { fbSignInInitiate, googleSignInInitiate, loginInitiate } from '../../userauth/actions';
import './login.css';
const Login = () => {
const [state, setState] = useState({
email: "",
password: "",
});
const { email, password } = state;
const { currentUser } = useSelector((state) => state.user);
const navigate = useNavigate();
const [loading, setLoading] = useState(false);
useEffect(() => {
if(currentUser || loading) {
navigate("/dashboard");
}
}, [currentUser, navigate]);
const dispatch = useDispatch();
const handleGoogleSignIn = () => {
dispatch(googleSignInInitiate());
setLoading(true);
};
const handleFBSignIn = () => {
dispatch(fbSignInInitiate());
setLoading(true);
};
const handleSubmit = (e) => {
e.preventDefault();
if(!email || !password) {
return;
}
dispatch(loginInitiate(email, password));
setState({ email: "", password: "" });
setLoading(true);
};
const handleChange = (e) => {
let { name, value } = e.target;
setState({...state, [name]: value });
};
return (
<div>
<div id="logreg-form">
<form className='form-signin' onSubmit={handleSubmit}>
<h1>
Sign in
</h1>
<div className="social-login">
<button
className='btn google-btn social-btn'
type='button'
onClick={handleGoogleSignIn}>
<span>
<i className='fab fa-google-plus-g'>Sign in with Google</i>
</span>
</button>
<button
className='btn facebook-btn social-btn'
type='button'
onClick={handleFBSignIn}>
<span>
<i className='fab fa-facebook-f'>Sign in with Facebook</i>
</span>
</button>
</div>
<p>OR</p>
<input
type="email"
id="inputEmail"
className='form-control'
placeholder='Email Address'
name="email"
onChange={handleChange}
value={email}
required
/>
<input
type="password"
id="inputPassword"
className='form-control'
placeholder='Password'
name="password"
onChange={handleChange}
value={password}
required
/>
<button
className='btn btn-secondary btn-block'
type="submit">
<i className="fas fa-sign-in-alt"></i>Sign In
</button>
<hr />
<p>Don't have an account</p>
<Link to="/signup">
<button
className='btn btn-primary btn-block'
type="button" id="btn-signup">
<i className='fas fa-user-plus'></i>Sign up New Account
</button>
</Link>
</form>
</div>
</div>
)
}
export default Login;
Dashboard.js:
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { logoutInitiate } from '../../userauth/actions';
const Dashboard = () => {
const { currentUser } = useSelector((state) => state.user);
const dispatch = useDispatch();
const handleAuth = () => {
if(currentUser) {
dispatch(logoutInitiate());
}
}
return (
<div>
<h1>User Dashboard</h1>
<br />
<button className='btn btn-danger' onClick={handleAuth}>Logout</button>
</div>
)
}
export default Dashboard
protectedRoute.js:
import React from 'react';
import { useSelector } from 'react-redux';
import LoadingToRedirect from './LoadingToRedirect';
const ProtectedRoute = ({children}) => {
const { currentUser } = useSelector((state) => ({...state.user}));
return currentUser ? children : <LoadingToRedirect />;
}
export default ProtectedRoute;
App.js:
import React, { useEffect, useState } from 'react';
import { Landing, Login, Signup, Contact, Dashboard, Error } from './pages';
import ProtectedRoute from './components/ProtectedRoute';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import { useDispatch } from "react-redux";
import { auth } from "./Firebase";
import { setUser } from './userauth/actions';
import './App.css';
const App = () => {
const dispatch = useDispatch();
useEffect(() => {
auth.onAuthStateChanged((authUser) => {
if(authUser) {
dispatch(setUser(authUser));
} else {
dispatch(setUser(null));
}
})
}, [dispatch]);
return (
<BrowserRouter>
<div className='App'>
<Routes>
<Route exact path="/" element={<Landing/>} />
<Route path="/login" element={<Login/>} />
<Route path="/signup" element={<Signup/>} />
<Route path="/contact" element={<Contact/>} />
<Route
path="/dashboard"
element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
}
/>
<Route path="*" element={<Error />} />
</Routes>
</div>
</BrowserRouter>
)
}
export default App;
If anyone comes across the same situation, I was able to solve this problem on my own and it's a simple fix. The problem was that the user authorization status wasn't updated whenever I called on Protected Routes but instead it just used the initial state when App.js was called. To get the user authorization status, I saved the user auth in my localStorage. Then, whenever I called user auth status in my redirects, I just fetched from my local storage.

REACT/REDUX Action not getting dispatched

What I am tying to do is when the user clicks on sign in button my action gets dispatch with email and password.
But, my action is not getting dispatched. Like when I checked my redux-dev-tools it is not showing anything:
There are no error message in console. I checked other answer's but nothing helped.
Here is the source code:
LoginScreen.js
import React, { useState, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import ErrorMessage from "../../components/ErrorMessage/ErrorMessage";
import Loader from "../../components/Loader/Loader";
import { login } from "../../redux/actions/userActions";
import "./LoginScreen.scss";
const LoginScreen = ({ location, history }) => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const dispatch = useDispatch();
const userLogin = useSelector((state) => state.userLogin);
const { loading, error, userInfo } = userLogin;
const redirect = location.search ? location.search.split("=")[1] : "/";
useEffect(() => {
if (userInfo) {
history.push(redirect);
}
}, [history, userInfo, redirect]);
const submitHandler = (e) => {
e.preventDefault();
dispatch(login(email, password));
};
return (
<>
<div className="login-container">
<div className="login-form">
<h1>Login</h1>
{loading ? (
<Loader />
) : error ? (
<ErrorMessage error={error} />
) : (
<form onSubmit={submitHandler}>
<div className="login-form-items">
<input
className="login-input"
type="email"
placeholder="Email address"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<input
className="login-input"
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button type="submit" value="submit">
Login
</button>
<h4>OR</h4>
<div className="login-form-social">
<button className="social">
<img
className="googleLogo"
src="/logo/google.svg"
alt="G"
/>{" "}
Login with Google
</button>
<button className="social social-github">
<img
className="githubLogo"
src="/logo/github.svg"
alt="GH"
/>{" "}
Login with GitHub
</button>
</div>
</div>
</form>
)}
</div>
</div>
</>
);
};
export default LoginScreen;
userAction.js
import axios from "axios";
import {
USER_LOGIN_FAIL,
USER_LOGIN_REQUEST,
USER_LOGIN_SUCCESS,
} from "../constants/userConstants";
export const login = () => (email, password) => async (dispatch) => {
try {
dispatch({
type: USER_LOGIN_REQUEST,
});
const config = {
headers: {
"Content-Type": "appllication/json",
},
};
const { data } = await axios.post(
"/api/users/login",
{ email, password },
config
);
dispatch({
type: USER_LOGIN_SUCCESS,
payload: data,
});
localStorage.setItem("userInfo", JSON.stringify(data));
} catch (error) {
dispatch({
type: USER_LOGIN_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
});
}
};
userReducer.js
import {
USER_LOGIN_FAIL,
USER_LOGIN_REQUEST,
USER_LOGIN_SUCCESS,
USER_LOGOUT,
} from "../constants/userConstants";
export const userLoginReducer = (state = {}, action) => {
switch (action.type) {
case USER_LOGIN_REQUEST:
return { loading: true };
case USER_LOGIN_SUCCESS:
return { loading: false, userInfo: action.payload };
case USER_LOGIN_FAIL:
return { loading: false, error: action.payload };
case USER_LOGOUT:
return {};
default:
return state;
}
};
store.js
import { createStore, combineReducers, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";
// reducers
import { userLoginReducer } from "./reducers/userReducers";
const reducer = combineReducers({
userLogin: userLoginReducer,
});
const userInfoFromStorage = localStorage.getItem("userInfo")
? JSON.parse(localStorage.getItem("userInfo"))
: null;
const initialState = {
userLogin: { userInfo: userInfoFromStorage },
};
const middleware = [thunk];
const store = createStore(
reducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
You've defined your action wrong. With redux-thunk you define your actions like this:
export const login = (email, password) => async (dispatch) => {
// your action code
};
// The above code is equivalent to
export const login = (email, password) => {
return async (dispatch) => {
// your action code
}
}
Not like this:
export const login = () => (email, password) => async (dispatch) => {
// your action code
};
// The above code is equivalent to
export const login = () => {
return (email, password) => {
return async (dispatch) => { // this is wrong
}
}
}
So your action is returning a function which then returns another function.
The way you use it caught my attention. Out of general use. Generally, api operations are done with packages such as saga or thunk. Action is only used as a hyperlink. I suggest you review the article below. I think this build will solve your problem.
https://blog.devgenius.io/reactjs-simple-understanding-redux-with-redux-saga-f635e273e24a

Action doesn't update the store

|I have the following component based on this:
**WarningModal.js**
import React from 'react';
import ReactDOM from 'react-dom';
import {connect, Provider} from 'react-redux';
import PropTypes from 'prop-types';
import {Alert, No} from './pure/Icons/Icons';
import Button from './pure/Button/Button';
import Modal from './pure/Modal/Modal';
import {setWarning} from '../actions/app/appActions';
import configureStore from '../store/configureStore';
const store = configureStore();
export const WarningModal = (props) => {
const {message, withCleanup} = props;
const [
title,
text,
leave,
cancel
] = message.split('|');
const handleOnClick = () => {
props.setWarning(false);
withCleanup(true);
}
return(
<Modal>
<header>{title}</header>
<p>{text}</p>
<Alert />
<div className="modal__buttons-wrapper modal__buttons-wrapper--center">
<button
onClick={() => withCleanup(false)}
className="button modal__close-button button--icon button--icon-only button--text-link"
>
<No />
</button>
<Button id="leave-warning-button" className="button--transparent-bg" onClick={() => handleOnClick()}>{leave}</Button>
<Button id="cancel-warning-button" onClick={() => withCleanup(false)}>{cancel}</Button>
</div>
</Modal>
);
}
WarningModal.propTypes = {
withCleanup: PropTypes.func.isRequired,
message: PropTypes.string.isRequired,
setWarning: PropTypes.func.isRequired
};
const mapStateToProps = state => {
console.log(state)
return {
isWarning: state.app.isWarning
}
};
const WarningModalContainer = connect(mapStateToProps, {
setWarning
})(WarningModal);
export default (message, callback) => {
const modal = document.createElement('div');
document.body.appendChild(modal);
const withCleanup = (answer) => {
ReactDOM.unmountComponentAtNode(modal);
document.body.removeChild(modal);
callback(answer);
};
ReactDOM.render(
<Provider store={store}>
<WarningModalContainer
message={message}
withCleanup={withCleanup}
/>
</Provider>,
modal
);
};
the issue I have is that 'setWarning' doesn't update the state, it does get called as I have a debugger inside the action and the reducer but the actual property doesn't not change to 'false' when:
props.setWarning(false);
gets called.
I use the following to trigger the custom modal:
const togglePromptCondition =
location.hash === '#access-templates' || location.hash === '#security-groups'
? promptCondition
: isFormDirty || isWarning;
<Prompt message={promptMessage} when={togglePromptCondition} />
To test this even further I have added 2 buttons in the application to toggle 'isWarning' (the state property I am talking about) and it works as expected.
I think that although WarningModal is connected in actual fact it isn't.
REDUCER
...
case SET_WARNING:
console.log('reducer called: ', action)
return {
...state,
isWarning: action.payload
};
...
ACTION
...
export const setWarning = status => {
console.log('action called')
return {
type: SET_WARNING,
payload: status
}
};
...
UPDATE
After having to incorporates the following:
const mapStateToProps = state => {
return {
isWarning: state.app.isWarning
}
};
const mapDispatchToProps = dispatch => {
return {
setWarning: (status) => dispatch({ type: 'SET_WARNING', payload: status })
}
};
I am now getting:
Maybe this could help?
You have to dispatch the actions in the action creator and the type of the action to dispatch should be always string.
Try this
const mapStateToProps = state => {
console.log(state)
return {
isWarning: state.app.isWarning
}
};
const mapDispatchToProps = dispatch => {
console.log(dispatch)
return {
setWarning: (status) => dispatch({ type: 'SET_WARNING', payload: status })
}
};
const WarningModalContainer = connect(mapStateToProps, mapDispatchToProps)(WarningModal);
REDUCER
...
case 'SET_WARNING':
console.log('reducer called: ', action)
return {
...state,
isWarning: action.payload
};
...

Actions must be plain objects. Trying to dispatch an actions into a redux-form

I'm trying to execute an async function when a form is submitted with redux-form, but no matter what, the error:
Actions must be plain objects. Use custom middleware for async actions.
Is always fired... here is my config:
createStore:
export const store = createStore(
rootReducer,
compose(
applyMiddleware(thunk),
window.devToolsExtension ? window.devToolsExtension() : f => f
)
);
The actions with thunk:
const loginUser = userData => {
return {
type: fb_actions.LOGIN,
payload: userData
}
};
export const loginFirebase = (email, password) => {
firebase.auth()
.signInWithEmailAndPassword(email, password)
.then(authData => {
return dispatch => {
dispatch(loginUser({email: email, password: password}))
}
})
};
The component:
import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';
import logo from '../static/logo-horizontal-black.svg';
import '../styles/Login.scss';
import renderLoginField from '../helpers/renderLoginField';
class Login extends Component {
constructor() {
super();
this.onFormSubmit = this.onFormSubmit.bind(this);
}
onFormSubmit(fields) {
const { login } = this.props;
login(fields.email, fields.password);
};
render() {
const {handleSubmit} = this.props;
return (
<div className='row'>
<div className='col-xs-12 col-sm-6 col-md-4 col-md-offset-4 col-md-offset-3'>
<section className='panel panel-default panel-login'>
<div className='panel-heading'>
<img src={logo} role='presentation' alt='cc-logo' className='img-responsive cc-logo' />
</div>
<div className='panel-body'>
<form onSubmit={handleSubmit(this.onFormSubmit)}>
<Field icon='mail' type='email' component={renderLoginField} name='email' />
<Field icon='password' type='password' component={renderLoginField} name='password' />
<button type='submit' className='btn btn-primary login-btn'>Login</button>
</form>
</div>
</section>
</div>
</div>
);
}
};
Login = reduxForm({ form: 'loginForm' })(Login);
export default Login;
And the container:
import { connect } from 'react-redux';
import Login from '../components/Login';
import { loginFirebase } from '../actions/firebaseActions';
const mapStateToProps = (state, ownProps) => {
return {
authData: state.auth
}
}
const mapDispatchToProps = (dispatch, ownProps) => {
return {
login: (email, password) => dispatch(loginFirebase(email, password))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Login);
Any idea about this?
I guess the action has to be chained as below,
export const loginFirebase = (email, password) => dispatch =>
firebase.auth()
.signInWithEmailAndPassword(email, password)
.then( authData => dispatch(loginUser({ email: email, password: password})));

Action does not trigger a reducer in React + Redux

I'm working on a react-redux app and for some reason the action I call does not reach the reducer (in which I currently only have a log statement). I have attached the code I feel is relevant and any contributions would be highly appreciated.
Action called within function in component:
onSearchPressed() {
console.log('search pressed');
this.props.addToSaved();
}
actions/index.js:
var actions = exports = module.exports
exports.ADD_SAVED = "ADD_SAVED";
exports.addToSaved = function addToSaved() {
console.log('got to ADD_SAVED step 2');
return {
type: actions.ADD_SAVED
}
}
reducers/items.js:
const {
ADD_SAVED
} = require('../actions/index')
const initialState = {
savedList: []
}
module.exports = function items(state = initialState, action) {
let list
switch (action.type) {
case ADD_SAVED:
console.log('GOT to Step 3');
return state;
default:
console.log('got to default');
return state;
}
}
reducers/index.js:
const { combineReducers } = require('redux')
const items = require('./items')
const rootReducer = combineReducers({
items: items
})
module.exports = rootReducer
store/configure-store.js:
import { createStore } from 'redux'
import rootReducer from '../reducers'
let store = createStore(rootReducer)
EDIT: Entire component for onSearchPressed:
class MainView extends Component {
onSearchPressed() {
this.props.addToSaved();
}
render() {
console.log('MainView clicked');
var property = this.props.property;
return (
<View style={styles.container}>
<Image style={styles.image}
source={{uri: property.img_url}} />
<Text style={styles.description}>{property.summary}</Text>
<TouchableHighlight style = {styles.button}
onPress={this.onSearchPressed.bind(this)}
underlayColor='#99d9f4'>
<Text style = {styles.buttonText}>Save</Text>
</TouchableHighlight>
</View>
);
}
}
module.exports = MainView;
As Rick Jolly mentioned in the comments on your question, your onSearchPressed() function isn't actually dispatching that action, because addToSaved() simply returns an action object - it doesn't dispatch anything.
If you want to dispatch actions from a component, you should use react-redux to connect your component(s) to redux. For example:
const { connect } = require('react-redux')
class MainView extends Component {
onSearchPressed() {
this.props.dispatchAddToSaved();
}
render() {...}
}
const mapDispatchToProps = (dispatch) => {
return {
dispatchAddToSaved: () => dispatch(addToSaved())
}
}
module.exports = connect(null, mapDispatchToProps)(MainView)
See the 'Usage With React' section of the Redux docs for more information.
Recently I faced issue like this and found that I had used action import but it has to come from props. Check out all uses of toggleAddContactModal. In my case I had missed toggleAddContactModal from destructuring statement which caused this issue.
import React from 'react'
import ReactDOM from 'react-dom'
import Modal from 'react-modal'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import {
fetchContacts,
addContact,
toggleAddContactModal
} from '../../modules/contacts'
import ContactList from "../../components/contactList";
Modal.setAppElement('#root')
class Contacts extends React.Component {
componentDidMount(){
this.props.fetchContacts();
}
render(){
const {fetchContacts, isFetching, contacts,
error, isAdding, addContact, isRegisterModalOpen,
toggleAddContactModal} = this.props;
let firstName;
let lastName;
const handleAddContact = (e) => {
e.preventDefault();
if (!firstName.value.trim() || !lastName.value.trim()) {
return
}
addContact({ firstName : firstName.value, lastName: lastName.value});
};
return (
<div>
<h1>Contacts</h1>
<div>
<button onClick={fetchContacts} disabled={isFetching}>
Get contacts
</button>
<button onClick={toggleAddContactModal}>
Add contact
</button>
</div>
<Modal isOpen={isRegisterModalOpen} onRequestClose={toggleAddContactModal}>
<input type="text" name="firstName" placeholder="First name" ref={node =>
(firstName = node)} ></input>
<input type="text" name="lastName" placeholder="Last name" ref={node =>
(lastName = node)} ></input>
<button onClick={handleAddContact} disabled={isAdding}>
Save
</button>
</Modal>
<p>{error}</p>
<p>Total {contacts.length} contacts</p>
<div>
<ContactList contacts={contacts} />
</div>
</div>
);
}
}
const mapStateToProps = ({ contactInfo }) => {
console.log(contactInfo)
return ({
isAdding: contactInfo.isAdding,
error: contactInfo.error,
contacts: contactInfo.contacts,
isFetching: contactInfo.isFetching,
isRegisterModalOpen: contactInfo.isRegisterModalOpen
});
}
const mapDispatchToProps = dispatch =>
bindActionCreators(
{
fetchContacts,
addContact,
toggleAddContactModal
},
dispatch
)
export default connect(
mapStateToProps,
mapDispatchToProps
)(Contacts)

Categories

Resources