please excuse potentially noob question, am new to react and react-redux.
I have a component representing a login screen currently. One of its props is "login", a dictionary that contains the email and password. After defining the component, I use the react-redux library to connect it with the store like so:
const mapStateToProps = (state) => {
return {
rootNav: state.rootNav,
login: state.login,
};
};
const mapDispatchToProps = (dispatch, ownProps) => {
return {
onLoginClick: () => {
// TODO: actually get the login credentials from the view
dispatch(actions.submitLogin('testuser', 'testpw'));
dispatch(actions.changeRootNav(rootNavs.OVERVIEW_NAV))
},
onEmailUpdate: (email) => dispatch(actions.updateEmail(email)),
onPasswordUpdate: (password) => dispatch(actions.updatePassword(password)),
};
};
Obviously, in the line dispatch(actions.submitLogin('testuser', 'testpw')); I want to have the real email and password submitted as a payload with the action. But I don't understand how I should be accessing it from the component (i.e. I can't just use this.props.login) or if/how I should be accessing it from the store (where would I pass the store in?)
Any clarification would be extremely helpful!
I think this can be handled in two ways. mapDispatchToProps is passed as the second argument to the react-redux connect function. It gives the connected component access to certain action creators. In this case you're giving it the action creators onLoginClick, onEmailUpdate, and onPAsswordUpdate.
Those functions are now accessible in your component via this.props.onLoginClick, this.props.onEmailUpdate etc. An easy solution is to create an onClick event on your login button, or onSubmit of the login form. If you're updating your Email and Password on your redux state and passing them to this component as props you can do something like this:
In your login class:
login() {
// get password and email from the props
const pw = this.props.password;
const email = this.props.email;
// submit login action with email and password
this.props.onLoginClick(email, password)
}
render() {
<form onSubmit={this.login.bind(this)}>
...
</form>
}
And update mapDispatchToProps to have onLoginClick expect an email and password.
const mapDispatchToProps = (dispatch, ownProps) => {
return {
// update this action creator to take an email and password
onLoginClick: (email, password) => {
// TODO: actually get the login credentials from the view
dispatch(actions.submitLogin(email, password));
dispatch(actions.changeRootNav(rootNavs.OVERVIEW_NAV))
},
onEmailUpdate: (email) => dispatch(actions.updateEmail(email)),
onPasswordUpdate: (password) => dispatch(actions.updatePassword(password)),
};
Option 2
Otherwise according to the react-redux docs here https://github.com/reactjs/react-redux/blob/master/docs/api.md you can also use the second argument of mapDispatchToProps, ownProps.
So you can change onLoginClick to look like this:
onLoginClick: () => {
const email = ownProps.email;
const password = ownProps.password;
dispatch(actions.submitLogin(email, password));
dispatch(actions.changeRootNav(rootNavs.OVERVIEW_NAV))
}
and on your form you can do this:
render() {
<form onSubmit={this.props.onLoginClick}>
...
</form>
}
or if you want it to be only on a button click...
<button onClick={this.props.onLoginClick}>Login</button>
Related
I'm trying to redirecting user after they successfully logged in by dispatching "AUTH_SUCCESS" action and gave me their tokens.There are several ways of doing this but I want your advise.Which one is the best way to redirect them after login? Whether using something in actions or in functional components.NOTE: I'm using functional components. Thanks!
Pseudocode
For thunk just pass this.props.history in your login event
Front end somewhere..
login = (e) => {
e.preventDefault();
const creds = { username: username, password: password}
this.props.login(creds, this.props.history);
}
Thunk
export const login = (credentials, history) => {
return (dispatch) => {
.....
/// after successful
history.push('/dashboard);
}
}
I have dashboard which should show data if a user is logged in and other data if no user is logged in. I already managed to figure out if a user is logged in it is not reflected on the page. It only changes after reloading the page.
This is what I have: An Account object with a userstatus component to hold details of the user. The Account object is placed in a context that is wrapped in the App.js. It also has a getSession function which gets the user details from the authentication mechanism. getSession also updates the userstatus according to the result (logged_in or not_logged_in). Second I have a dashboard component which runs the getSession method and puts the result in the console. Everythings fine. But the render function did not get the changed userstatus.
This is my code (Accounts.js):
export const AccountContext = createContext();
export const Account = {
userstatus: {
loggedinStatus: "not_logged_in",
values: {},
touched: {},
errors: {}
},
getSession: async () =>
await new Promise((resolve, reject) => {
...
}
}),
}
This is the Dashboard.js:
const Dashboard = () => {
const [status, setStatus] = useState();
const { getSession, userstatus } = useContext(AccountContext);
getSession()
.then(session => {
console.log('Dashboard Session:', session);
userstatus.loggedinStatus = "logged_in"
setStatus(1)
})
.catch(() => {
console.log('No Session found.');
userstatus.loggedinStatus = "not_logged_in"
setStatus(0);
});
const classes = useStyles();
return (
<div className={classes.root}>
{userstatus.loggedinStatus}
{status}
{userstatus.loggedinStatus === "logged_in" ? 'User logged in': 'not logged in'}
<Grid
container
spacing={4}
...
I already tried with useState and useEffect, both without luck. The userstatus seems to be the most logical, however, it does not update automatically. How can I reflect the current state in the Dashboard (and other components)?
React only re-renders component when any state change occur.
userstatus is simply a variable whose changes does not reflect for react. Either you should use userstatusas your app state or you can pass it in CreateContext and then use reducers for update. Once any of two ways you use, you would see react's render function reflect the changes in userstatus.
For how to use Context API, refer docs
I am really confused!!
So, I wand to dispatch another action inside of action:
import * as actionsTypes from '../constants/actionsTypes';
export const loginUserSuccess = userName => ({
type: actionsTypes.LOGIN_SUCCESS,
userName
});
export const loginUserFailed = () => ({
type: actionsTypes.LOGIN_FAILED
});
export const loginUser = userName => dispatch => {
dispatch(loginUserSuccess(userName));
}
Inside of loginUser action I will create a logic to choose which action to dispatch. It is doesn`t matter for now
Here is usage of loginUser action
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { loginUser } from '../../actions';
class loginUserForm extends Component {
state = {
userName: ''
}
inputChangeHandler = event => {
this.setState({ userName: event.target.value })
}
loginUserHandler = event => {
event.preventDefault();
this.props.loginUser(this.state.userName);
}
render() {
return (
<form className="login-user" onSubmit={event => this.loginUserHandler(event)}>
<input type="text" onChange={event => this.inputChangeHandler(event)} />
<input type="submit" value="Login" disabled={this.state.userName.length === 0} />
</form>
)
}
};
const mapDispatchToProps = dispatch => ({
loginUser: userName => dispatch(loginUser(userName))
})
Nothing super-natural :) But!! I have an error
Actions must be plain objects. Use custom middleware for async
actions.
This is really fun! I don`t have an async code here!!
What a hell is going on...
loginUser() is a "thunk". That means that it's actually passing a function to the real store.dispatch(), and is not a plain action object.
Make sure that you've actually included the redux-thunk middleware as part of your store setup process - that teaches the store to accept passing functions to store.dispatch(). See the posts What the heck is a "thunk"? and Thunks in Redux: The Basics for explanations on what this means.
Also, as a side note, you can simplify that file a bit. connect supports an "object shorthand" for defining mapDispatch - you can pass an object full of action creators directly, like:
const mapDispatch = {loginUser};
The situation is I am creating a single board which will hold a collection of note cards (each note has an id, title and body), and each note card will have a button to delete it. Also the application will be syncing with firebase, so my main question is how to pass arguments to middlewares AND do it inside of mapDispatchToProps. The following is my code to point out where my success with middleware and where I am currently blocked.
To hydrate the app on startup, I dispatch a middleware function that gets the data from firebase, and then dispatches actions handled by reducers and finally gets updated by the container/presentation component.
Middleware function:
export function hydrateApp(dispatch) {
dispatch({type: 'PENDING'});
fireBaseDBRef.once('value').then(snapshot => {
let firebaseNotes = snapshot.val()
let notes = [];
// populate notes using firebaseNotes, nothing exciting
dispatch({ type: 'DONE', notes: notes });
// the 'DONE' action.type is handled by the reducer and passes data
// to the container component successfully
}).catch(e => {
dispatch({type: 'ERROR', error: e});
});
}
Container component:
const mapStateToProps = state => {
return {
notes: state.boardReducer.notes
};
};
const mapDispatchToProps = dispatch => {
return {
addNote: () => {
dispatch(boardMiddleware.createNote);
}
};
};
const BoardContainer = connect(
mapStateToProps,
mapDispatchToProps
)(BoardPresentation);
So far so good, and this is what I added to the same middleware and container component files to handle delete scenarios.
Middleware function:
export function deleteNote(id) {
return (dispatch) => {
dispatch({type: 'PENDING'});
//firebase stuff happening here
dispatch((type: 'DONE'});
}
}
Container component:
const mapDispatchToProps = dispatch => {
return {
addNote: () => {
dispatch(boardMiddleware.createNote);
},
removeNote: (id) => {
dispatch(boardMiddleware.deleteNote(id));
}
};
};
The problem is that deleteNote gets called non-stop on startup, I don't even need to click the button.
I know the code presented may not make a whole bunch of sense, but the crux of my problem is that I need to some how pass an id to the middleware function when the user clicks on the button, and because I'm passing the function as a prop, it for some reasons decides to just call it a million times.
I could call boardMiddleware.deleteNote function inside the presentation component just like the examples in the official redux page do, but I'm wondering if there is a way of doing it the way I'm trying to do.
I also thought about binding the argument into the middleware function, but that also doesn't feel right, something like this
removeNote: (id) => {
dispatch(boardMiddleware.deleteNote.bind(id));
}
Thanks for any help in advance!
Is it possible to get the params outside of a component props?
In my case I want handle navigation inside some action creators (with redux).
Let's say that you connected the action createUser to your component ok?
So from your component you are going to do this:
this.props.createUser(this.props.username, this.props.password)
And finally this is going to be your action dispatcher:
export const createUser = (username, password) => {
return (dispatch,getState) => {
dispatch({
type: CREATE_USER,
username: username
})
browserHistory.push(`/welcome-user/${username}`)
}
}