In this my action the user's log out of my application is exported in react
export const logoutUser = () => {
return dispatch => {
dispatch(
{
type: LOGOUT_USER
}
)
.then(() => {
logoutUserSuccess(dispatch, )
})
.catch((err) => {
logoutUserError(err, dispatch)
})
}
}
const logoutUserSuccess = (dispatch) => {
dispatch(
{
type: LOGOUT_USER_SUCCESS
}
)
AsyncStorage.removeItem('#token_jwt')
console.log('saiu')
Actions.loginScreen()
}
const logoutUserError = (err, dispatch) => {
dispatch(
{
type: LOGOUT_USER_ERROR
}
)
Alert.alert('Erro ao sair da conta')
console.log(err)
}
is my Reducer
case LOGOUT_USER:
return {
...state
}
case LOGOUT_USER_SUCCESS:
return {
INITIAL_STATE
}
case LOGOUT_USER_ERROR:
return {
...state
}
is my screen to logout
onLogout() {
this.props.logoutUser()
}
const mapStateToProps = state => (
{
email: state.Authentication.email
}
)
export default connect(mapStateToProps, { logoutUser })(Home)
The return is the following error
I put the email on the mapStateToProps, because I don't know how to leave it blank, what matters to me is the logout
You can try creating a mapDispatchToProps function and dispatch the action logoutUser from inside the function and pass it as a second argument to connect.
In doing so, you can invoke the LogoutUser from mapDispatchToProps in your onLogout function.
import {logoutUser} from './youractions.js'
onLogout() {
this.props.LogoutUser();
}
const mapDispatchToProps = (dispatch) => ({
LogoutUser: () => dispatch(logoutUser()),
});
export default connect(null, mapDispatchToProps)(Home);
Related
I'm a little confused on passing an object to the redux store. I have successfully created the store and can add items from the initial state. The function also fires when called
Action:
import { GET_ITEM } from './OrderTypes'
export const getItem = (payload) => {
return {
type: GET_ITEM,
payload: { payload }
}
}
Reducer:
import { GET_ITEM } from './OrderTypes'
const initialState = {
orderList: [],
}
const orderReducer = (state = initialState, action) => {
switch (action.type) {
case GET_ITEM: return {
...state,
orderList: [...state.orderList, action.payload]
}
default: return state
}
}
export default orderReducer
Component:
class TestComponentextends Component {
pushItem = () => {
this.props.getItem({
payload: 'test object'
})
}
render() {
return (
<input type='button' value='test btn' onClick={this.pushItem} />
)
}
}
const mapStateToProps = state => {
return {
orderList: state.orderList
}
}
const mapDispatchToProps = dispatch => {
return {
getItem: () => dispatch(getItem())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(TestComponent)
What happens: An empty object is added to the orderList array.
What I want to happen: Store the object in pushItem in the orderList array.
Your mapDispatchToProps doesn't pass the arguments to the action creator (see mapDispatchToProps function arguments - 2nd example):
const mapDispatchToProps = dispatch => ({
getItem: (...args) => dispatch(getItem(...args))
})
Even easier is to let react-redux handle the mapping by using mapDispatchToProps as an object:
const mapDispatchToProps = {
getItem
}
I'm trying to implement Google Authentication using GAPI and I want to store the userId of the user fetched by GAPI into the redux store. I don't know how to pass class component variables into a redux store or action dispatcher. Can someone please help me out to get out of this?
import { SIGN_IN, SIGN_OUT } from './types'
export const signIn = (dispatch, userId) => {
console.log('actions',userId)
dispatch(
{
type: SIGN_IN,
payload:userId
}
)
}
export const signOut = (dispatch) => {
dispatch(
{
type: SIGN_OUT
}
)
}
const mapStateToProps = (state,ownProps) => {
return {
isSignedIn: state.auth.isSignedIn
}
}
const mapDispatchToProps = (dispatch, ownProps) => {
return {
SignIn: () => signIn(dispatch),
SignOut: () => signOut(dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(GoogleAuth);
SignIn: () => signIn(dispatch,ownProps.toString()),
I think it's should be
SignIn: (value) => signIn(dispatch, value),
You can pass value when you actually dispatch an action. once you have the user id you do
this.props.SignIn(userId)
this will fire action dispatch where you have to pass value like
const mapDispatchToProps = (dispatch, ownProps) => {
return {
SignIn: (id) => signIn(dispatch,id),
SignOut: () => signOut(dispatch)
}
}
export const signIn = (userId) => {
return {
type: SIGN_IN,
payload: userId
};
};
export const signOut = () => {
return {
type: SIGN_OUT
};
};
For your GoogleAuth Component:
componentDidMount() {
window.gapi.load('client:auth2', () => {
window.gapi.client.init({
clientId: 'YOUR_CLIENT_ID',
scope: 'email'
}).then(() => {
this.auth = window.gapi.auth2.getAuthInstance(); //Assign the auth instance
this.onAuthChange(this.auth.isSignedIn.get()); //Update the auth state in store
this.auth.isSignedIn.listen(this.onAuthChange); //Wait for the auth state to change in future
});
});
}
onAuthChange = (isSignedIn) => {
if (isSignedIn) {
this.props.signIn(this.auth.currentUser.get().getId());
} else {
this.props.signOut();
}
};
const mapsStateToProps = (state) => {
return { isSignedIn: state.auth.isSignedIn };
}
export default connect(mapsStateToProps, { signIn, signOut })(GoogleAuth);
I'm switching my state to redux and ran into this error
TypeError: sourceSelector is not a function
I've pasted the code from the component and the action i'm dispatching, i think its something in mapDispatchToProps but not sure
component
componentDidMount() {
const { dispatch } = this.props;
dispatch(getTableData(this.props.apiUrl, this.state.limit, this.state.skip));
}
const mapStateToProps = ({tableData}) => ({
tableData,
});
function mapDispatchToProps (dispatch) {
return {
getTableData: (data) => dispatch(getTableData(data)),
}
}
export default connect(
mapStateToProps,
mapDispatchToProps,
{ getTableData }
)(SearchableTable);
action
import * as TYPES from './types';
import axios from 'axios';
export const getTableData = (url, limit, skip) => async dispatch => {
try {
dispatch({ type: TYPES.FETCH_TABLE_DATA_LOADING });
const response = await axios.post(url, {
limit: limit,
skip: skip,
});
await dispatch({
type: TYPES.FETCH_TABLE_DATA,
payload: response.data,
});
dispatch({ type: TYPES.FETCH_TABLE_DATA_FINISHED });
} catch (err) {
dispatch({ type: TYPES.INSERT_ERROR, payload: err.response });
}
};
Try this
import { bindActionCreators } from "redux";
import { connect } from "react-redux";
import { getTableData } from "actions";
componentDidMount() {
// Make sure you use this.props.getTable data and not the raw imported
// getTableData function
this.props.getTableData(this.props.apiUrl, this.state.limit, this.state.skip));
}
const mapStateToProps = state => ({
tableData: state.tableData
});
const mapDispatchToProps = dispatch =>
bindActionCreators(
{
getTableData
},
dispatch
);
return connect(
mapStateToProps,
mapDispatchToProps
)(SearchableTable);
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.
I am using redux-saga along with reactjs. I am using mapDispatchToProps to trigger onClick changes with values but I am getting above error saying that dispatch is not defined anyone please let me know how to resolve this in saga way?
import { connect } from 'react-redux';
import { updateModifiedValue } from "../actions/programActions";
import { bindActionCreators } from 'redux';
export class ProgramProfileContainer extends React.Component {
render() {
return (
<div>
<ProgramVolumeComponent
updateModifiedGrowth = {(value) => updateModifiedValue(value,this.props.match.params.program_id)}
/>
</div>
)
}
}
const mapStateToProps = (state) => {
console.log("update state", state)
return {
programProfileData: state.programDetailReducer.programDetails,
}
}
const mapDispatchToProps= (dispatch) => (
bindActionCreators({updateModifiedValue},dispatch)
)
export default connect(
mapStateToProps,
mapDispatchToProps)(ProgramProfileContainer)
this is my action
export function updateModifiedValue(Value,Id){
console.log("mval",Value)
console.log("id",Id)
return {
type:types.MODIFIED_GROWTH,
growthValue
}
}
here is my index saga
export default function* startForman() {
console.log("index saga")
yield fork(watcher);
}
watcher
export default function* watcher(){
yield takeLatest(types.LISTS,myCallSaaga)
}
and my calll saaga
export default function* myCallSaaga({Value,Id}){
const array=[Value,Id]
try {
const Data = yield call(growthdetail,...array)
yield [
put({ type: types.MODIFIED, Detail: Data })
]
} catch (error) {
yield put({
type: 'FETCH_ERROR',
error
});
}
}
and my reducer
export default function(state=[],action){
switch(action.type){
case types.PRO_LIST:
return [...state,action.List]
case types.MODIFIED:
return {...state,growth:4}
default:
return state
}
}
I think the error is that you are calling the action directly from your import and not from your props, which is what mapDispatchToProps is for. Try this:
import programActionsCreator from "../actions/programActions";
...
render() {
const { updateModifiedValue } = this.props.actions;
return() {
...
updateModifiedGrowth = {(value) => updateModifiedValue(value, this.props.match.params.program_id)}
...
}
...
const mapDispatchToProps= (dispatch) => ({
actions: bindActionCreators(programActionsCreator, dispatch)
})
from rowland's comment and from this github post I made the changes like this
const mapDispatchToProps= (dispatch) => {
return { dispatch,
...bindActionCreators({
updateModifiedValue
}, dispatch)}//bindActionCreators({updateModifiedValue},dispatch)
}
and
<ProgramVolumeComponent
updateModifiedGrowth = {(value) => this.props.updateModifiedValue(value,this.props.match.params.program_id)}
/>
basically we need to pass our own dispatch we are mapDispatchToProps