I want to use Redux in my registration page so I created a user reducer:
const user = (state = initialState, action) => {
switch (action.type) {
case 'TYPE_FIRSTNAME':
console.log('typed first name ' + action.text);
return { ...state, firstName: action.text };
case 'TYPE_LASTNAME':
return { ...state, lastName: action.text };
case 'TYPE_EMAIL':
return { ...state, email: action.text };
case 'TYPE_PASSWORD':
return { ...state, password: action.text };
default:
return state;
}
}
it is created like this:
const AppReducer = combineReducers({
nav,
user
});
export default AppReducer;
the nav reducer is for the navigation (used with react-navigation and it works fine). After that I created a container:
const mapStateToProps = state => {
return {
firstName: state.firstName,
lastName: state.lastName,
}
};
const mapDispatchToProps = (dispatch, ownProps) => ({
typeFirstName: (text) => {console.log('typed firstname');
dispatch({type: 'TYPE_FIRSTNAME', text})},
typeLastName: (text) => dispatch({type: 'TYPE_LASTNAME', text}),
registerUser: () => {
//register("mamad");
console.log('called register user : ');
dispatch({type: 'MAINSCREEN'})
}
});
export default connect(mapStateToProps,
mapDispatchToProps)(RegisterScene)
But it is never called, why?
The only problem I found is the mapStateToProps. I think it should be
const mapStateToProps = state => {
return {
firstName: state.user.firstName,
lastName: state.user.lastName,
}
};
It would be helpful if you put the error log here.
When you combine reducers the state gets put into the specified state branch.
In your case you need state.user.
so your mapStateToProps function should look like so:
const mapStateToProps = state => {
return {
firstName: state.user.firstName,
lastName: state.user.lastName,
}
};
Related
I need a help with updating user state.I register a user with name, email and password. Then in the profile page i want to give a chance to update(or create new) values like City and Country. And now im confused. My Redux action
export const updateUser=(profileId, updatedUser)=>async(dispatch)=>{
try {
const {data}= await api.updateUser(profileId,updatedUser)
dispatch({type: UPDATE_USER, payload: data})
} catch (error) {
console.log(error.message);
}
Reducer:
const initialState = {
users: [
{
city: "", country: "", email: "", name: "",
password: "",
_id: "",
},
],
};
const user = (state = initialState, action) => {
switch (action.type) {
case GET_ONE_USER:
return {
...state,
users: action.payload,
};
case UPDATE_USER:
return { ...state, users: action.payload };
default:
return state;
}
};
API:
export const updateUser=(profileId, updatedUser)=>API.patch(`/user/${profileId}`, updatedUser)
route:
router.patch('/:profileId',updateUser)
controller:
export const updateUser = async (req,res)=>{
const {id} = req.params
const {city, country} = req.body
const updatedUser={city, country}
try {
await User.findByIdAndUpdate(id,updatedUser, {new: true} )
res.status(200).json(updatedUser)
} catch (error) {
res.status(400).json({message: 'Blad'})
}
}
In my component:
const{ users} = useSelector((state)=>state.users)
and submit handler const handleSubmit =(e) =>{ e.preventDefault() dispatch(updateUser(users._id, data)) }
When i click button and dispatch an action, it only changes new values, all other are removed. I think that has something to do with my return state from reducer?
EDIT:
Ok, somehow i fixed this, although i think i could simplify the code?
case UPDATE_USER:
return { ...state, users: {...state.users, city:action.payload.city, country:action.payload.country}};
default:
return state;
You are updating state in a wrong way. You are replacing state with the new payload only. What you have to do is you have to keep previous state data and then add new payload that you are getting.
switch (action.type) {
.....
case UPDATE_USER:
return { ...state, users: [...state.users, action.payload] };
default:
return state;
}
};
My user structure is:
user = {
email: 'email',
flashcards: []
}
And i would like to add data into user's flashcards array (using redux)
My user-reducer
import { UserActionTypes } from './user.types';
const INITIAL_STATE = {
currentUser: null,
};
// GETS STATES OBJ AND RECIVES AN ACTION
const userReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case UserActionTypes.SET_CURRENT_USER:
return {
...state,
currentUser: action.payload,
};
case UserActionTypes.ADD_FLASHCARD:
return {
...state,
currentUser: action.payload,
};
default:
return state;
}
};
export default userReducer;
user-actions
export const addFlashCard = user => ({
type: UserActionTypes.ADD_FLASHCARD,
payload: user.flashcards,
});
And when i'm doing so my payload is undefined.
Could you give me some hints?
You are currently overwriting currentUser with the value of user.flashcards from the redux action. To add new flashcards, the ADD_FLASHCARD branch of your reducer should look more like this:
case UserActionTypes.ADD_FLASHCARD:
return {
...state,
currentUser: {
...state.currentUser,
flashcards: [
...state.currentUser.flashcards,
...action.payload
]
}
};
I'm trying to fetch props into my componentDidUpdate method.
Sadly second action from .then block doesn't dispatch.
I have the following:
My AddPerson.js which is a form to add Person. There I have following states:
constructor(props) {
super(props);
this.state = {
loading: false,
firstName: '',
secondName: '',
email: '',
date: '',
};
}
Whole is connected to redux:
function mapDispatchToProps(dispatch) {
return {
addPerson: data => dispatch(addPerson(data))
};
}
const mapStateToProps = state => {
return { data: state.data };
};
const Person = connect(
mapStateToProps,
mapDispatchToProps
)(AddPerson);
export default Person;
Then I have action dispatcher like that:
export const addPerson = (payload) => {
return dispatch => {
dispatch(addTodoStarted());
axios
.post(PATH + '/attendant', {
payload,
})
.then(res => {
dispatch(addTodoSuccess(res));
})
.catch(err => {
dispatch(addTodoFailure(err));
});
};
};
const addTodoSuccess = payload => ({
type: ADD_PERSON,
data: {
payload
}
});
const addTodoStarted = () => ({
type: ADD_PERSON,
data:
"loading"
});
const addTodoFailure = error => ({
type: ADD_PERSON,
data: {
error
}
});
And my reducer:
function reducer(state = {} , action) {
switch (action.type) {
case ADD_PERSON:
return Object.assign({}, state, {
data: action.data,
})
default:
return state;
}
}
export default reducer;
When fetch happens in the action, there is firstly dispatched type of action loading then after promise solves I want to dispatch action that is in .then block. What am I missing?
EDIT:
My componentDidUpdate looks like that:
componentDidUpdate(prevProps) {
console.log(prevProps)
if (prevProps.data !== this.state.data) {
console.log(prevProps.data)
}
}
I have this configuration when using react-redux connect().
const mapStateToProps = state => ({
...state
});
const mapDispatchToProps = dispatch => ({
addMessage: msg => dispatch(addMessage(msg))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(RoomChat);
When using this.props.chats I am getting "TypeError: Cannot read property 'map' of undefined".
This is the state, an array of objects that have the fields of 'username' and 'content':
chats = [{ content: '', username: '' }]
It is define in the RoomChat.js like this:
this.state = {
chats: []
};
This is the store.js where the redux store is defined:
import { createStore } from 'redux';
import MessageReducer from './reducers/MessageReducer';
function configureStore(chats = [{ content: '', username: '' }]) {
return createStore(MessageReducer, chats);
}
export default configureStore;
Reducer:
export default (state, action) => {
switch (action.type) {
case 'addMessage':
return {
content: action.text,
username: 'R'
};
default:
return state;
}
};
action:
export function addMessage(text) {
return { type: 'addMessage', text };
}
What went wrong here ?, I have tried multiple configurations without success so far
In your mapStateToProps function, you need to do this...
const mapStateToProps = state => ({
chats: state
});
This is because you're creating your store with the chats array when you do createStore(MessageReducer, chats).
So the state is automatically chats
UPDATE
In addition to #smoak's comment, you need to update your reducer like this
export default (state, action) => {
switch (action.type) {
case 'addMessage':
return [
...state,
{
content: action.text,
username: 'R'
}
];
default:
return state;
}
};
Mabye try this
const mapStateToProps = state => {return {chats: state.chats}};
or this
const mapStateToProps = state => { return {...state}};
You need to return object in mapStateToProps and mapDistpachToProps.
In my component I fire my action:
submitForm(e) {
const language = e.target.value;
this.props.actions.addLanguage(language, 'language', '2');
}
and connect to redux:
const mapStateToProps = state => ({
UserDetails: state.UserDetails,
});
const mapDispatchToProps = dispatch => ({
actions: bindActionCreators(UserActions, dispatch),
});
export default connect(mapStateToProps, mapDispatchToProps)(Screen1);
Actions/index:
import * as types from '../constants/ActionTypes';
export const addDetails = (age, gender, ethnicity) => ({
type: types.ADD_DETAILS,
age,
gender,
ethnicity,
});
export const addLanguage = (value, language, page) => ({
type: types.ADD_LANGUAGE,
value,
language,
page,
});
export const addAccessCode = (value, field) => ({
type: types.ADD_ACCESSCODE,
value,
field,
});
UserDetails:
import {
ADD_DETAILS,
ADD_ACCESSCODE,
ADD_LANGUAGE,
ADD_DRINKS,
} from '../constants/ActionTypes';
const initialState = {
id: 0,
language: '',
session: '',
values: '',
accessCode: '',
age: 0,
gender: '',
ethnicity: '',
drinkOften: '',
drinkConcern: '',
};
export default function UserDetails(state = initialState, action) {
debugger;
return (dispatch, state) => {
switch (action.type) {
case ADD_LANGUAGE:
this.props.router.push(`${action.page}`);
return {
...state,
[action.field]: action.value,
};
case ADD_ACCESSCODE:
return {
...state,
[action.field]: action.value,
};
case ADD_DETAILS:
return {
...state,
ethnicity: action.ethnicity,
gender: action.gender,
age: action.age,
};
case ADD_DRINKS:
return {
...state,
[action.field]: action.value,
};
default:
return state;
}
};
}
Any ideas?
#Ravindra Ranwala I can get the action to fire with your suggestion, but its still undefined in the reducer, any ideas?
Using my debugger, the action is actually going in, but my reducer can't get past the thunk return (dispatch, getState) => {
Add return statement to your action. That will solve your issue. It's like this.
export const toggleTodo = id => {
return {
type: 'TOGGLE_TODO',
id
}
}
Hope this helps. Happy coding !
Remove return (dispatch, getState) => { }, you only need to switch statement inside your reducer. The below is an example of how your reducer should look as taken from the redux docs.
function todoApp(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
visibilityFilter: action.filter
})
default:
return state
}
}