Looking for guidance using redux-token-auth. Throwing this Type Error:
Error
All the creators' examples involve making the calls from the Component class like so:
// EXAMPLE: components/SignInScreen.jsx
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { signInUser } from '../redux-token-auth-config' // <-- note this is YOUR file, not the redux-token-auth NPM module
class SignInScreen extends Component {
constructor (props) { ... }
...
submitForm (e) {
e.preventDefault()
const { signInUser } = this.props
const {
email,
password,
} = this.state
signInUser({ email, password }) // <-<-<-<-<- here's the important part <-<-<-<-<-
.then(...)
.catch(...)
}
render () {
const { submitForm } = this
<div>
<form onSubmit={submitForm}>...</form>
</div>
}
}
export default connect(
null,
{ signInUser },
)(SignInScreen)
Is it feasible to move the calls up to an action file? In documentation, he mentions that
registerUser, signInUser, and signOutUser are Redux Thunk actions and
thus, when wired through mapDispatchToProps return Promises.
I'm digging through the source code, but I can't figure out what changes when the sign in dispatch is mapped via Redux rather than directly imported and called. If anyone is familiar with this extension, any ideas would be much appreciated!
This is my attempt that throws the error:
// /actions/auth.js
import { signInUser, signOutUser } from '../redux-token-auth-config'
export const Login = (email, password) => {
return (dispatch) => {
dispatch(LoginStart());
signInUser({ email, password })
.then((response) => dispatch(LoginSuccess(response.data.data)))
.catch((error) => dispatch(LoginError(error)));
};
};
export const LoginStart = () => ({
type: 'LOGIN::START'
});
export const LoginSuccess = (data) => ({
type: 'LOGIN::SUCCESS',
payload: data
});
export const LoginError = (error) => ({
type: 'LOGIN::ERROR',
payload: error
})
export const Logout = () => {
return (dispatch) => {
dispatch(SessionCleanup())
signOutUser()
.then((response) => console.log('Success'))
.catch((error) => console.log(error))
}
}
export const SessionCleanup = () => ({
type: 'LOGIN::SESSION_CLEANUP'
})
Assuming you are trying to call Login from a component I had the same issue and fixed it by doing the following:
export default connect(state => ({}), { signInUser })(FooBar);
When I called the action I passed along signInUser.
this.props.fooBarBaz(email, password, signInUser);
This allowed me to use signInUser outside of the component just as I did within it.
So in your case it should be as simple as keeping:
export default connect(
null,
{ signInUser },
)(SignInScreen)
and calling Login like:
Login(email, password, signInUser);
Related
I've been trying to dispatch a function that will call an async parse cloud function. It worked well in my other projects when i used them in functions. But this is the first time i'm using them in a component and when i call the dispatch from map dispatch to props, I get this error. Please help me out.
ProfileHeader.js
import React, { Component } from 'react';
import Cover_Image from './Cover_Image.jpg';
import Profile_Pic from './Profile_Pic.svg';
import './ProfileHeader.css';
import { connect } from 'react-redux';
import { fetchUserProfile } from '../../Redux/UserProfile-Redux/UserProfileActionMethods';
class ProfileHeader extends Component {
componentDidMount() {
this.props.fetchUserProfile()
}
render() {
return (
<div className="profile-header-layout"></div>
)
}
}
const mapStatetoProps = (state) => {
return {
profile: state.UserProfile
}
}
const mapDispatchtoProps = (dispatch) => {
return {
fetchUserProfile: () => { dispatch(fetchUserProfile()) }, dispatch,
}
}
export default connect(mapDispatchtoProps, mapStatetoProps)(ProfileHeader)
The action Method:
import Parse from 'parse/dist/parse.min.js';
import { FETCH_USERPROFILE_FAILURE, FETCH_USERPROFILE_REQUEST, FETCH_USERPROFILE_SUCCESS } from './UserProfileActions';
const params = { username: "prvnngrj" }
export const fetchUserProfileRequest = () => {
return {
type: FETCH_USERPROFILE_REQUEST
}
}
export const fetchUserProfileSuccess = (userprofiles) => {
return {
type: FETCH_USERPROFILE_SUCCESS,
payload: userprofiles
}
}
export const fetchUserProfileFailure = (error) => {
return {
type: FETCH_USERPROFILE_FAILURE,
payload: error
}
}
export const fetchUserProfile = () => {
return async dispatch => {
dispatch(fetchUserProfileRequest)
try {
const responsedata = await Parse.Cloud.run("GetUserProfileForUsername", params);
const userprofiles = responsedata;
dispatch(fetchUserProfileSuccess(userprofiles))
}
catch (error) {
const errorMessage = error.message
dispatch(fetchUserProfileFailure(errorMessage))
}
}
}
Please ignore parts of code which do not make it relevant, its straight from the project
You mixed up the order of your arguments, so this.props.dispatch is actually your state!
You need to change
export default connect(mapDispatchtoProps, mapStatetoProps)(ProfileHeader)
to:
export default connect(mapStatetoProps, mapDispatchtoProps)(ProfileHeader)
If you can switch to function components and the useSelector/useDispatch hooks you should. This is the current recommended approach and it's easier to use.
nativeusingreact-redux,react-thunk,handleActionswithducks structure` and trying to dispatch action function to change state.
It worked actually until this morning, but it doesn't work anymore.
I have no idea what I changed. Even worse, I didn't commit because this project is for practicing react native, so I cannot undo my work.
If I'm right that I understood correctly, dispatch of connect in container component should call fetchName() in categoryImgListMod.js(action).
However, I guess dispatch never works here.
So state never changes.
If you give me any of advice, it would be very helpful for me, and I would appreciate you.
Here's my code
categoryListContainer.js
import React, {Component} from 'react';
import {View} from 'react-native';
import { connect } from 'react-redux';
import CategoryImgList from '../components/categoryImgList';
import * as CategoryImgActions from '../store/modules/categoryImgListMod';
class CategoryImgListContainer extends Component {
loadNames = async () => {
console.log(this.props);
const { CategoryImgActions } = this.props;
try {
await CategoryImgActions.fetchName();
} catch (e) {
console.log(e);
}
}
render() {
const {container} = styles;
const { loadNames } = this;
return (
<View style={container}>
<CategoryImgList names={loadNames}/>
</View>
);
}
}
const styles = {
container: {
height: '100%'
}
}
export default connect(
({categoryImgListMod}) => ({
name: categoryImgListMod.name
}),
(dispatch) => ({
fetchName: () => {
dispatch(CategoryImgActions.fetchName())
}
})
)(CategoryImgListContainer);
categoryImgListMod.js
import {handleActions} from 'redux-actions';
// firestore
import * as db from '../../shared';
// Action types
const GET_CATEGORY_NAME_PENDING = 'categoryImgList/GET_CATEGORY_NAME_PENDING';
const GET_CATEGORY_NAME_SUCCESS = 'categoryImgList/GET_CATEGORY_NAME_SUCCESS';
const GET_CATEGORY_NAME_FAILURE = 'categoryImgList/GET_CATEGORY_NAME_FAILURE';
// action creator
export const fetchName = () => async (dispatch) => {
dispatch({type: GET_CATEGORY_NAME_PENDING});
try {
const response = await db.getCategoryNames();
const arr = [];
response.docs.forEach(res => {
arr.push(res.id);
});
dispatch({type: GET_CATEGORY_NAME_SUCCESS, payload: arr});
return arr;
} catch (e) {
console.log(e);
dispatch({type: GET_CATEGORY_NAME_FAILURE, payload: e});
}
}
const initialState = {
fetching: false,
error: false,
name: []
};
// Reducer
export default handleActions({
[GET_CATEGORY_NAME_PENDING]: (state) => ({ ...state, fetching: true, error: false }),
[GET_CATEGORY_NAME_SUCCESS]: (state, action) => ({ ...state, fetching: false, name: action.payload }),
[GET_CATEGORY_NAME_FAILURE]: (state) => ({ ...state, fetching: false, error: true })
}, initialState);
I solved using bindActionCreators.
But, still I don't understand why it never dispatched.
(dispatch) => ({
CategoryImgActions: bindActionCreators(categoryImgActions, dispatch)
})
I believe its the curly braces which are the problem, try this:
(dispatch) => ({
fetchName: () => dispatch(CategoryImgActions.fetchName())
})
If you are using curly braces you need to explicitly return:
(dispatch) => ({
fetchName: () => {
return dispatch(CategoryImgActions.fetchName());
}
})
I can't lie, i'm a bit confused about react-redux. I think lot of the actions require parameters (for an example deleting items from the store), but even if i'm still reading about how to dispatch from component in that way to pass a parameter, about 2 hours now, i didn't get any answers. I was tried with this.props.dispatch and with mapDispatchToProps, i always get the this.props... is not a function message. Here's what i'm trying to do:
const getTheArray = (array) => {
return {
type: 'GET_ARRAY',
array
}
}
class Example extends......
componentDidUpdate(){
//i have a button, and when it clicked, turns the status: 'deleted'
if (this.state.status === 'deleted'){
fetch('http://localhost:3001/deleteitem', {
method: 'post',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
id: this.props.id
})
})
.then(res => res.json())
.then(data => this.props.deleteFromArray(data))
.catch(err => console.log(err))
}
}
function mapStateToProps(state) {
return {
array: state.array
};
}
function mapDispatchToProps(dispatch) {
return({
deleteFromArray: (array) => {dispatch(getTheArray(array))}
})
}
export default connect(mapStateToProps, mapDispatchToProps)(Example);
It isn't the only place where i need to dispatch with an action, where the action's object's properties depending on another property passed to the function, so i really want to do, what's the best way to pass property to action, and dispatch it within a react component.
import { bindActionCreators } from "redux"
function mapDispatchToProps(dispatch) {
return(bindActionCreators({
deleteFromArray: (array) => {getTheArray(array)}
}, dispatch))
}
In your dumbComponents, just call e.g this.props.deleteFromArray([1,2,3,4])
This should solve the issue. you are not binding with dispatch
use of react-redux
you need to import action first
import { storeProfiles } from 'actions/profiles';
define props
const {
storeProfiles
} = props;
get data use of userEffect
useEffect(() => {
fetchProfiles().then(storeProfiles);
}, [ fetchProfiles, storeProfiles ]);
map to props
const mapDispatchToProps = dispatch => ({
storeProfiles: x => dispatch(storeProfiles(x))
});
export default connect(mapStateToProps, mapDispatchToProps)(Component);
Read official documentation
Using es6:
const mapDispatchToProps = (dispatch) => ({ addToCart: (params) => dispatch(actions.addToCart(params)), });
Actually, you don't have to pass params to actions like that, in my opinion.
I'm using React Hooks with TypeScript and Redux and thunk. The way I delete a post using an action creator looks like this:
PostDetails.tsx component:
import {deletePost, getPost, LanguageActionTypeEnum} from "../../../../app/store/aspian-core/actions";
interface IPostDetailsProps {
getPost: Function;
// action for deleting a post
deletePost: Function;
loadingInitial: boolean;
submitting: boolean;
post: IPost | undefined;
lang: LanguageActionTypeEnum;
}
const PostDetails: FC<PostDetailsProps> = ({
match, getPost,
lang, loadingInitial,
submitting, deletePost, post
}) => {
}
// To delete a post
const onDeleteBtnClick = async (id: string) => {
try {
/// using deletePost action and passing id to it
await deletePost(id);
message.success(t('messages.post-deleting-success'));
} catch (error) {
message.error(t('messages.post-deleting-error'));
}
};
return (
// omitted for brevity
<Popconfirm
key={uuidv4()}
title={t('popconfirm.title')}
/// calling onDeleteBtnClick() here
onConfirm={() => onDeleteBtnClick(post!.id)}
okText={t('popconfirm.ok-text')}
cancelText={t('popconfirm.cancel-text')}
placement={lang === LanguageActionTypeEnum.en ? 'left' : 'right'}
okButtonProps={{
danger: true,
}}
>
<Button
loading={submitting}
size="small"
type="primary"
icon={<DeleteFilled/>}
danger
>
{t('buttons.delete')}
</Button>
</Popconfirm>
// omitted for brevity
)
}
// Redux State To Map
const mapStateToProps = ({post, locale}: IStoreState): { post: IPost | undefined, submitting: boolean, loadingInitial: boolean, lang: LanguageActionTypeEnum } => {
return {
post: post.post,
submitting: post.submitting,
loadingInitial: post.loadingInitial,
lang: locale.lang
}
}
// Redux Dispatch To Map
const mapDispatchToProps = {
getPost,
// mapped deletePost action to props here
// and I will be able to pass id to its argument later
deletePost
}
export default connect(mapStateToProps, mapDispatchToProps)(PostDetails);
and my the action creator looks like this:
postActions.ts:
export const deletePost = (id: string) => async (dispatch: Dispatch) => {
try {
dispatch(setPostSubmitting(true));
await agent.Posts.delete([id]);
dispatch<IDeletePostAction>({
type: PostActionTypes.DELETE_SINGLE_POST,
payload: {
id,
submitting: false
}
})
} catch (error) {
console.log(error);
dispatch(setPostSubmitting(false));
throw error;
}
}
And it works fine. Hopefully it could help you with your situation.
If you use actions as arrow function so there would be no need to bindActionsCreators, let me do it with a very simple way.
your action should be of arrow function like so:
export const formHandler=({key,value})=>{
/*for checking that you get the values on function call or not*/
console.log(key,value)
return {
type:'formHandler',
payload:{key,value},
}
}
the below action function takes to arguments so you have to pass likewise
const mapDispatchToProps = dispatch=> {
/*this will add the functions as a props to the components
which can be trigger on order to change the state*/
return {
inputHandler: ({key,value})=>dispatch(formHandler({key,value}))
}
}
at the end you can use connect on yourComponent
export default connect(mapStatesToProps,mapDispatchToProps)(yourComponent)
then in your component
you can call the function using
class component
this.props.inputHandler({key:'k',value:2})
functional component
props.inputHandler({key:'k',value:2})
After changing one of my components, (Profile.js) from a class to a function to simplify and have cleaner code, the onClick triggering of a redux action (like) no longer does anything.
Some have pointed out the action needs to be map differently, but I'm not sure why as I'm still new to redux and it's confusing as to why it works fine as a class but not as a function.
What adds to the confusion is that I'm also using react thunk to make things async.
User.js
import { fetchUser, like } from '../../actions/userActions';
class User extends React.Component {
componentDidMount() {
const { username } = this.props.match.params;
this.props.fetchUser(username);
}
render() {
const { like, user } = this.props;
return (
<Profile user={user} like={like} />
)
}
}
const mapStateToProps = state => ({
user: state.store.user
});
export default connect(mapStateToProps, {fetchUser, like})(User);
Profile.js Before
import { like, user } from '../../actions/userActions';
class Profile extends React.Component {
const { like, user } = this.props
return (
<a onClick={() => like(user.username)}>Like</a>
)
}
export default connect (mapStateToProps, {like}){Profile)
Profile.js After
const Profile = (props) => {
const { like, user } = props
return (
<a onClick={() => like(user.username)}>Like</a>
)
}
actions.js
const url = 'http://localhost:3001'
function handleErrors(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}
export const like = (username) => dispatch => {
fetch(`${url}/like/${username}`, {credentials: 'include', method: 'post'})
.then(handleErrors)
.then(res => res.json())
.then(res =>
dispatch({
type: LIKE,
payload: res
})
)
.catch(error => console.error('Error:', error))
}
export const fetchUser = (username) => dispatch => {
fetch(`${url}/${username}`, {credentials: 'include'})
.then(handleErrors)
.then(res => res.json())
.then(res =>
dispatch({
type: FETCH_USER,
payload: res
})
)
.catch(error => console.error('Error:', error))
}
reducers.js
export default function(state = initialState, action) {
switch (action.type) {
case FETCH_USER:
return {
...state,
user: action.payload.user
};
case LIKE:
return {
...state,
user: {
...state.user,
meta: {
...state.user.meta,
like: action.payload.like
}
}
};
store.js
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
compose(
applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
);
export default store;
console.log of like in Profile.js
const Profile = (props) => {
const { user, like } = props
console.log(like)
ƒ like(username) {
return function (dispatch) {
fetch(url + '/like/' + username, { credentials: 'include', method: 'post' }).then(handleErrors).then(function (res) {
return res.json();
…
If I were to create a normal function such as
const test = () => { console.log('test') }
and change the onClick={} in Profile.js to use that, it works fine.
You should create handler in the User component, call your action creator there and pass it as a callback to child Profile component.
So, your code will looks like:
import { like } from '../../actions/userActions';
class User extends React.Component {
...
onClickHandler = username => {
return () => {
this.props.like(username);
}
}
render() {
const { user } = this.props;
return <Profile user={user} onClickHandler={this.onClickHandler} />
}
}
const mapStateToProps = state => ({
user: state.store.user
});
export default connect(mapStateToProps, {fetchUser, like})(User);
Then, call onClickHandler in your Profile component:
const Profile = props => {
const { onClickHandler, user } = props;
return (
<button onClick={onClickHandler(user.username)}>Like</button>
)
}
Hope it will helps.
This is LoginScreenContainer.js
import React from 'react'
import { connect } from 'react-redux'
import LoginScreen from '../components/LoginScreen.js'
import * as AUTH_ACTIONS from '../actions/auth.js'
const mapStateToProps = state => ({
loggedIn: state.AUTH.loggedIn
})
const mapDispatchToProps = (dispatch) => {
loginDefault: (username , password) => {
dispatch(AUTH_ACTIONS.actions.loginDefault(username, password))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(LoginScreen)
This is actions/auth.js
import types from '../utilities/types.js'
export const actions = {
loginDefault: (username, password) => ({
type: types.LOGIN_DEFAULT,
meta: {
type: 'api',
path: '/users/token',
method: 'POST'
},
payload: {username, password}
})
};
export default actions
What is the best way to debug this.
I cannot figure out which part goes wrong.
I have been thinking this for 3 days.
Guidance and help needed.
Thank you.
(I am fairly new to react)
Your dispatch lacks the return value, you should change it to
const mapDispatchToProps = (dispatch) => ( { // <- forgot the wrapping with ( here
loginDefault: (username , password) => {
dispatch(AUTH_ACTIONS.actions.loginDefault(username, password))
}
} ) // <- forgot closing of the wrapping with ) here
This is due to the nature of the arrow functions, and you seem to do it correctly for the state to props, so I believe it's a small oversight.
So, an arrow function that has this
const sample = argument => { return { test: '1' } }
equals to
const sample = argument => ( { test: '1' } );
But not to
const sample = argument => { test: '1' };
So if you use an arrow function, and you wish to return an object, you should either return it, or wrap it with ( ) brackets
I might be wrong but I think that mapDispatchToProps is incorrect:
const mapDispatchToProps = () => ({
loginDefault: AUTH_ACTIONS.actions.loginDefault,
})
And then in your event handler:
this.props.loginDefault(username, password)