How to push data in object? - javascript

I'm using Redux in my React App, and I need to put data to object (using action).
I have such reducer and action:
case GET_USER_DATA: {
return {
...state,
user: action.value // user - is object
}
}
//my action
export const getUserData = (value) => ({type: 'GET_USER_DATA',value})
And then I need to put data to my Redux state using action, but it not works, can anybody explaine me how to do this?
func:
authListener() {
fire.auth().onAuthStateChanged((user) => {
console.log(user);
if (user) {
getUserData(user) // I need to put it here
localStorage.setItem('user', user.uid);
} else {
getUserData(null)
localStorage.removeItem('user');
}
});
}

Have you added this function in mapDispatchToProps so that it can get the dispatch function and reducer can be called?
const mapStateToProps = (state: any) => {
return {
//any data from state
};
};
function mapDispatchToProps(dispatch: any) {
return bindActionCreators({
getUserData
}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(YOUR_COMPONENT_NAME);
You can now call the function as
props.getUserData(user);

Related

Dispatch is not a function in reducer

MY action
const fetchDataApi = (getState) => {
let { data } = getState()
return axios.get('http://api.openweathermap.org/data/2.5/weather?q=london,uk&appid=26aacf43db7ecfa2ecd85500eaee9920').then(thunkdata => {
console.log(thunkdata)
return {
[data]: thunkdata
}
})
}
const fetchgetDataCall = () => {
return (dispatch, getState) => {
return dispatch(fetchDataApi(getState))
}
}
export const getData = (dispatch) => {
dispatch(fetchgetDataCall())
return {
type: actionTypes.GETDATA,
}
}
In action.js i want to get data from my whether api and store in data, so i am using getstate to get data variable and assign data to it
My calender Component where i am connecting my callender to actionType
import React, { Component } from 'react';
// import 'moment/locale/it.js';
import { DatePicker, DatePickerInput } from 'rc-datepicker';
// import { ca } from 'date-fns/esm/locale';
import 'rc-datepicker/lib/style.css';
import { connect } from 'react-redux';
import { getData } from '../store/actions/actions'
const date = '2015-06-26' // or Date or Moment.js
class Callender extends Component {
//These is a method es7
onChangeandler = (jsDate, dateString, event) => {
// event.preventDefault()
console.log("[we are lokking at js date]",jsDate);
this.props.getWether();
console.log("[we are seeing the props storeDta]",this.props.storeData);
}
//Next method
render() {
return (
<div>
<DatePicker onChange={this.onChangeandler} value={date} />
</div>
)
}
}
const mapStateToProps = state =>({
storeData: state.data
})
const mapDispatchToProps = (dispatch) =>({
getWether: () => dispatch(getData())
})
export default connect(mapStateToProps,mapDispatchToProps)(Callender)
My reducer
import * as actionType from '../actions/actionTypes';
const intialState ={
time:null,
day:null,
data:null
}
// reducer
const reducer = (state=intialState, action) =>{
switch(action.type){
case actionType.GETDATA:
return {
...state,
data:action.data
}
case actionType.POSTDATA:
return {
...state
}
default :
return {
...state
}
}
}
export default reducer;
actionTypes.js
export const POSTDATA="POSTDATA";
export const GETDATA = "GETDATA";
1)I am calling my action creator in callender.js file
2) Where i am using thunk middleware to get data ,and store in data variable from redux store
3)I can't find the issue please help me
Your actions looks quite weird. The getData action creator disptaches fetchgetDataCall which dispatches fetchDataApi and that returns just some object { [data]: thunkdata} where property data are probably null in that moment. Hence there are not any properties type or data in your action object.
The second thing what your getData do is returning the object {type: actionTypes.GETDATA}, hence there is not any property data in your action object.
Try to do it something like this (updated according to #mbojko answer):
const getData = () => {
return (dispatch) => {
return axios.get('http://api.openweathermap.org/data/2.5/weather?q=london,uk&appid=26aacf43db7ecfa2ecd85500eaee9920').then(thunkdata => {
return dispatch({
type: actionTypes.GETDATA,
data: thunkdata
})
})
}
}
Compare your function signature
export const getData = (dispatch) => {
With how you call it:
const mapDispatchToProps = (dispatch) =>({
getWether: () => dispatch(getData())
})
The argument is missing (therefore dispatch is not defined and obviously not a function).
Should be dispatch(getData(dispatch)), probably.

dispatch is not returnig promise i think?

I want to write a CRUD in react-redux, and have a problem with multiple dispatch. I think my dispatch is not returning a promise?
my error is "Uncaught TypeError: dispatch(...).then is not a function", on this line:
fetchPost: (id) => {
dispatch(fetchPost(id))
.then((result) => ...
Action
export function fetchPost(id) {
const request = axios.get(`${ROOT_URL}/posts/details/${id}`);
console.log(request);
return {
type: "FETCH_POST",
payload: request
}
}
export function fetchPostSuccess(post) {
return{
type: "FETCH_POST_SUCCESS",
payload: post
}
}
export function fetchPostError(error) {
return{
type: "FETCH_POST_ERROR",
payload: error
}
}
Reducer
case "FETCH_POST":
return {
...state,
loading: true,
activePost: state.activePost
}
case "FETCH_POST_SUCCESS":
return {
...state,
activePost: action.payload
}
case "FETCH_POST_ERROR":
return {
...state,
activePost: []
}
Component
class Details extends React.Component {
constructor(props){
super(props);
}
componentDidMount() {
this.props.fetchPost(this.props.detail_id.id);
}
render() {
return (
<div>
Details page
<ul>
<li >
{this.props.detail_id.id}
</li>
</ul>
</div>
)
}
}
Container
const mapStateToProps = (state, ownProps) => ({
posts: state.posts,
});
const mapDispatchToProps = dispatch => {
return {
fetchPost: (id) => {
dispatch(fetchPost(id))
.then((result) => {
if (result.payload.response && result.payload.response.status !== 200){
dispatch(fetchPostError(result.payload.response.data));
} else {
dispatch(fetchPostSuccess(result.payload.data));
}
})
},
resetMe: () => {
console.log('reset me');
}
};
};
const GetDetails = connect(
mapStateToProps,
mapDispatchToProps
)(Details)
I just want to pick post from postlist and show details on enother page...Hope someone help me to fix this issue
Saga
export function* fetchProducts() {
try {
console.log('saga')
const posts = yield call(api_fetchPost);
console.log(posts);
yield put({ type: "FETCH_SUCCESS", posts});
} catch (e) {
yield put({ type: "FETCH_FAILD", e});
return;
}
}
export function* watchFetchProducts() {
yield takeEvery("FETCH_POSTS", fetchProducts)
}
According to the Redux documentation, dispatch() returns the dispatched action, i.e. it's argument. The dispatched action is just a plain object which describes an action.
The promise is returned by Axios' get() method which is just an alias for axios() method.
Both call of the asynchronous method and the promise resolving should be done in Redux action. There is Redux Thunk middleware to handle such asynchronous actions. With Thunk middleware, you may return a function from your action. The function takes single argument dispatch, which is Redux's dispatch() function which you can call from functions resolving a promise.
With Redux Thunk middleware, your action fetchPost() will take the following view:
export function fetchPost(id) {
return function(dispatch) {
dispatch({type: 'FETCH_POST'})
axios.get(`${ROOT_URL}/posts/details/${id}`)
.then(function(response) {
if (response.status === 200){
dispatch({type: 'FETCH_POST_SUCCESS', payload: response.data})
} else {
dispatch({type: 'FETCH_POST_ERROR', payload: respose.data})
}
})
.catch(function(error) {
dispatch({type: 'FETCH_POST_ERROR', payload: error})
})
}
}
Your actions fetchPostSuccess() and fetchPostError() are unnecessary.

How to get data back from reducer with a async action

Redux newbie here
So I have this code that dispatches an async action and it uses promise to handle the request
So here is mapDispatchToProps
const mapDispatchToProps = (dispatch) => {
return {
fetchUserList: () => {
console.log('---In fetchUserList---');
dispatch({
type: FETCH_USER_LIST,
payload: new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
console.log('xhr: ', xhr);
xhr.open('GET', 'http://localhost:5000/data/fetch/users');
xhr.onload = () => {
console.log(' --- in onload function ---');
console.log(xhr.responseText);
resolve(JSON.parse(xhr.responseText));
}
xhr.onerror = () => {
console.log(' --- in onerror function ---');
reject(xhr.statusText);
}
xhr.send();
})
});
}
what it does is it fetches array of json objects from the server which get map to
mapStateToProps
const mapStateToProps = (state) => {
return {
userlist: state.list_reducer,
};
};
Now here is the component I used both of above:
class Profile extends Component {
constructor(props) {
super(props);
this.state = {
listRender: false
};
}
render() {
console.log('In render: ');
console.log(this.props.userlist);
return (
// basic list rendering template
);
}
componentDidMount() {
this.props.fetchUserList();
this.setState({
listRender: true
});
}
Now It can see from the above that I use fetchUserList() in componentDidMount() to fetch data from the server and data does get send to the reducer
Here is the list_reducer.js:
export default function list_reducer(state = {
operation: '',
userlist: []
}, action) {
switch (action.type) {
case "FETCH_USER_FULFILLED":
console.log("FETCH_USER_FULFILLED");
console.log(action.payload);
return {
operation: 'success',
userlist: action.payload
}
case "UPDATE_USER_DETAILS_FULFILLED":
return {
operation: 'success',
userlist: action.payload
}
case "DELETE_USER_FULFILLED":
return {
operation: 'success',
userlist: action.payload
}
case "REGISTER_USER_FULFILLED":
return {
operation: 'success',
//userlist:
}
default:
return { ...state
}
}
};
Now instead of receiving newly fetched userlist I get the default data that is passed to reducer in this.props.userlist (mapStateToProps)
So the question is how do I get the newly fetched list instead of the default state data that is given to the reducer.
When making async calls with Redux I use Thunks
By default, Redux action creators don’t support asynchronous actions
like fetching data, so here’s where we utilise Redux Thunk. Thunk
allows you to write action creators that return a function instead of
an action. The inner function can receive the store methods dispatch
and getState as parameters.
So, if you wanted to make an API call, our action would look like this.
Actions.js (or your equivalent)
import axios from "axios";
export const receiveUserLift = json = {
return {
type: "RECEIVE_USERS",
payload: json
}
}
export function fetchUserList() {
return dispatch => {
return axios
.get('http://localhost:5000/data/fetch/users')
.then(response => {
dispatch(receiveUserLift(response))
})
.catch(error => console.log("FetchUserList Axios Error", error))
}
}
Reducer would include the following
case "RECEIVE_USERS":
return Object.assign({}, currentState, {
userList: action.payload
});
Component would include the following
import { fetchUserList } from "WHERE WE DECLARED OUR ARE ACTION";
class Example extends React.Component {
componentDidMount() {
// You could use it anywhere, but this is how you would call the action
this.props.fetch()
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.users === prevState.users) return null;
return {
users: nextProps.users
};
}
}
const mapStateToProps = (state, ownProps) => {
return {
users: state.userList
};
};
const mapDispatchToProps = (dispatch, ownProps) => ({
fetch: () => dispatch(fetchUserList())
});
Sidenote
Receiving the data in our component asynchronously requires that we make use of component lifecycle methods. You will see a lot of guides around the internet advising to use componentWillReceiveProps to do this, but this is going to be removed in future versions of React and replaced with static getDerivedStateFromProps you can read more about it here
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.users === prevState.users) return null;
return {
users: nextProps.users
};
}
The above code serves as a rough guide, but it is the general pattern that I've followed when dealing with Async API calls and rendering with Redux

Accessing a part of reducer state from one reducer within another reducer

I do not know how to access a boolean isLoading flag from reducerForm.js reducer in reducerRegister.js. I have used combineReducers() and I use isLoading to disable a button during form submit.
It's initial state is false, after clicking submit, it changes to true. After the form submission is successful, isLoading is reset to false again. Below is the relevant code for this issue:
actionRegister.js
let _registerUserFailure = (payload) => {
return {
type: types.SAVE_USER_FAILURE,
payload
};
};
let _registerUserSuccess = (payload) => {
return {
type: types.SAVE_USER_SUCCESS,
payload,
is_Active: 0,
isLoading:true
};
};
let _hideNotification = (payload) => {
return {
type: types.HIDE_NOTIFICATION,
payload: ''
};
};
// asynchronous helpers
export function registerUser({ // use redux-thunk for asynchronous dispatch
timezone,
password,
passwordConfirmation,
email,
name
}) {
return dispatch => {
axios.all([axios.post('/auth/signup', {
timezone,
password,
passwordConfirmation,
email,
name,
is_Active: 0
})
// axios.post('/send', {email})
])
.then(axios.spread(res => {
dispatch(_registerUserSuccess(res.data.message));
dispatch(formReset());
setTimeout(() => {
dispatch(_hideNotification(res.data.message));
}, 10000);
}))
.catch(res => {
// BE validation and passport error message
dispatch(_registerUserFailure(res.data.message));
setTimeout(() => {
dispatch(_hideNotification(res.data.message));
}, 10000);
});
};
}
actionForm.js
export function formUpdate(name, value) {
return {
type: types.FORM_UPDATE_VALUE,
name, //shorthand from name:name introduced in ES2016
value
};
}
export function formReset() {
return {
type: types.FORM_RESET
};
}
reducerRegister.js
const INITIAL_STATE = {
error:{},
is_Active:false,
isLoading:false
};
const reducerSignup = (state = INITIAL_STATE , action) => {
switch(action.type) {
case types.SAVE_USER_SUCCESS:
return { ...state, is_Active:false, isLoading: true, error: { register: action.payload }};
case types.SAVE_USER_FAILURE:
return { ...state, error: { register: action.payload }};
case types.HIDE_NOTIFICATION:
return { ...state , error:{} };
}
return state;
};
export default reducerSignup;
reducerForm.js
const INITIAL_STATE = {
values: {}
};
const reducerUpdate = (state = INITIAL_STATE, action) => {
switch (action.type) {
case types.FORM_UPDATE_VALUE:
return Object.assign({}, state, {
values: Object.assign({}, state.values, {
[action.name]: action.value,
})
});
case types.FORM_RESET:
return INITIAL_STATE;
// here I need isLoading value from reducerRegister.js
}
return state;
};
export default reducerUpdate;
reducerCombined.js
import { combineReducers } from 'redux';
import reducerRegister from './reducerRegister';
import reducerLogin from './reducerLogin';
import reducerForm from './reducerForm';
const rootReducer = combineReducers({
signup:reducerRegister,
signin: reducerLogin,
form: reducerForm
});
export default rootReducer;
This is where I use isLoading:
let isLoading = this.props.isLoading;
<FormGroup>
<Col smOffset={4} sm={8}>
<Button type="submit" disabled={isLoading}
onClick={!isLoading ? isLoading : null}
>
{ isLoading ? 'Creating...' : 'Create New Account'}
</Button>
</Col>
</FormGroup>
Mapping state to props within the same component
function mapStateToProps(state) {
return {
errorMessage: state.signup.error,
isLoading: state.signup.isLoading,
values: state.form.values
};
}
This is covered in the Redux FAQ at https://redux.js.org/faq/reducers#how-do-i-share-state-between-two-reducers-do-i-have-to-use-combinereducers:
Many users later want to try to share data between two reducers, but find that combineReducers does not allow them to do so. There are several approaches that can be used:
If a reducer needs to know data from another slice of state, the state tree shape may need to be reorganized so that a single reducer is handling more of the data.
You may need to write some custom functions for handling some of these actions. This may require replacing combineReducers with your own top-level reducer function. You can also use a utility such as reduce-reducers to run combineReducers to handle most actions, but also run a more specialized reducer for specific actions that cross state slices.
Async action creators such as redux-thunk have access to the entire state through getState(). An action creator can retrieve additional data from the state and put it in an action, so that each reducer has enough information to update its own state slice.
A reducer cannot access another reducer's state, but if you're using redux-thunk you can do so from within an action creator. As an example, you can define an action creator like this:
export const someAction = () =>
(dispatch, getState) => {
const someVal = getState().someReducer.someVal;
dispatch({ type: types.SOME_ACTION, valFromOtherReducer: someVal });
};
React Redux works on unidirectional data flow.
Action ---> Reducer /store ---> Reducer
Reducer works on small subset of store, you can not access store inside reducer which is not part of Reducer. you can either need to fire new action from the component based on reducer state return.

Can not see updated state

I have the following action:
export function loginUserRequest() {
console.log('ACTION CALLED');
return {
type: LOGIN_USER_REQUEST,
};
}
and this is the reducer:
export default function loginReducer(state = initialState, action) {
switch (action.type) {
case LOGIN_USER_REQUEST:
console.log('REDUCER CALLED');
return Object.assign({}, state, {
isAuthenticated: true,
isAuthenticating: true,
statusText: null,
});
default:
return initialState;
}
}
Then, my component:
class Login extends React.Component {
goHome = () => {
browserHistory.push('/');
}
handleSubmit = (values) => {
console.log(this.props.isAuthenticating);
this.props.actions.loginUserRequest();
console.log(this.props.isAuthenticating);
}
render() {
return (
<LoginForm onSubmit={this.handleSubmit} />
);
}
}
Login.propTypes = {
actions: PropTypes.objectOf(PropTypes.func).isRequired,
isAuthenticating: PropTypes.bool.isRequired,
};
const mapStateToProps = state => ({
token: state.login.token,
isAuthenticated: state.login.isAuthenticated,
isAuthenticating: state.login.isAuthenticating,
});
const mapDispatchToProps = dispatch => ({
actions: bindActionCreators(actionCreators, dispatch),
});
export default connect(mapStateToProps, mapDispatchToProps)(Login);
LoginForm is a redux-form component.
So, the expeted ouput from the handleSubmit function is:
false
ACTION CALLED
REDUCER CALLED
true
but it is giving me:
false
ACTION CALLED
REDUCER CALLED
false
But in the redux dev tool I can see the diff in LOGIN_USER_REQUEST:
Why I don't see it inside the handleSubmit function? Is it something related to redux-form library?
Extra info:
Added shouldComponentUpdate and logger
shouldComponentUpdate = (nextProps, nextState) => {
console.log('Should component update called');
if (this.props.isAuthenticating !== nextProps.isAuthenticating) {
console.log('distntict');
return true;
}
console.log('false');
return false;
}
You are getting such a result because of async nature of Javascript. So in your code
handleSubmit = (values) => {
console.log(this.props.isAuthenticating);
this.props.actions.loginUserRequest();
console.log(this.props.isAuthenticating);
}
First, you are printing the value of prop, and then the action gets called but before the action returns a response with the updated state, your third statement gets called to log the value and since the state is not yet updated you see the same result.
One approach will be have callbacks but that doesn't seem to be a requirement for your case. If your want to log the state then you can do so in componentWillReceiveProps function
like
componentWillReceiveProps(nextProps) {
if(this.props.isAuthenicating != nextProps.isAuthenticating) {
console.log(nextProps.isAuthenticating);
}
}
I hope it helps

Categories