how to use asyn actions in redux - javascript

I want to know when my action has finished the request so I can treat data ( Kind of a promise ).
I'm using thunk to dispatch function.
Here is my action
export function addUser(nom,url) {
return (dispatch) =>{
axios.post('/', {
nom: nom,
url: url
})
.then(function (response) {
dispatch(()=>{//somthing for the reducer})
console.log(response);
})
}
and in my component I want to perform something like this
addUser('test','test')
.then() // do something after the addUser is executed

The way we do this is in redux is by dispatching an action on success like this:
const addUserSuccess = () => {
return {
type: 'ADD_USER_SUCCESS',
}
}
export function addUser(nom,url) {
return (dispatch) =>{
axios.post('/', {
nom: nom,
url: url
})
.then(function (response) {
dispatch(addUserSuccess());
console.log(response);
})
}
Now in your reducer to something like this:
const initialState = { addedUser: false };
export default (state = initialState, action) => {
switch (action.type) {
case 'ADD_USER_SUCCESS:
return {
...state,
addedUser: true
};
default:
return state;
}
}
Last but not least, connect your component to the store.
class ExampleComponent extends React.Component {
componentDidMount() {
addUser();
}
render() {
if (props.addedUser) {
// do something after the addUser is executed
}
return <div>Example</div>;
}
}
const mapStateToProps = (state) => ({
addedUser: state.user.addedUser
});
// connect is a react-redux HOC
const ConnectedComponent = connect(mapStateToProps)(ExampleComponent);
I know this is a lot of boilerplate but this is just a very basic overview. Find out more at Async Actions in the redux docs.
Update:
If you what to work with the promise instead, you could do the following:
export function addUser(nom, url) {
return (dispatch) =>{
return axios.post('/', {
nom: nom,
url: url
})
.then(function (response) {
dispatch(addUserSuccess());
console.log(response);
})
}
Then you could use it in the component.
addUser()().then();
Just make sure to call it twice, because addUser() is a function that returns a function that returns a promise

Related

Data sent from service-file is not updated in Reducer State

I am like in a strange problem. The problem is that I am trying to make an API hit (in service file) which in turn provides some data (it is working), this data is to be updated in my reducer1.js and then returned. Now, my issue is though the value is coming in reducer file, but is not returned, so in turn, state is not changed, and in turn my end component is not rerendered.
Now, when my service file is successfully hitting and then returning data to my reducer1.js, why in the world the updated-state is not returned by "GET_List" action type? Can someone see any problem?
index.js (service file)
const global = {
getActressList: async function(){
const response = await fetch("http://localhost:2000/api/actressList");
const data = await response.json();
return data;
}
}
export default global;
reducer1.js
import global from '../../services/index';
const initialState = {
data: [
{
id: 1,
name: "Aishwarya Rai",
src: "/assets/img/aishwarya.png"
}
]
};
function reducer1(state = initialState, action) {
switch (action.type) {
case "GET_LIST": {
const data = global.getActressList();
data.then((res)=> {
return {
...state,
data: res
}
})
}
default:
return state;
}
}
export default reducer1;
Result:
You are returning from a promise not from a reducer function:
function reducer1(state = initialState, action) {
switch (action.type) {
case "GET_LIST": {
const data = global.getActressList();
data.then((res) => {
// here you are returning from a promise not from a reducer function
return {
...state,
data: res,
};
});
}
default:
return state;
}
}
The code in reducer should be sync like this:
function reducer1(state = initialState, action) {
switch (action.type) {
case "GET_LIST": {
return {
...state,
data: action.payload,
};
}
default:
return state;
}
}
And your data fetching should be moved to component effect like this:
function YourComponent() {
const dispatch = useDispatch();
const data = useSelector(state => state.data)
useEffect(() => {
const data = global.getActressList();
data.then((res) => {
dispatch({type: 'GET_LIST', payload: res});
});
}, [])
...
}
EDIT
If you use class components the fetching logic should be placed in componentDidMount lifecycle hook like this:
class YourComponent extends Component {
state = { data: [] };
componentDidMount() {
const data = global.getActressList();
data.then((res) => {
dispatchYourAction({type: 'GET_LIST', payload: res});
});
}
...
}

How to push data in object?

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);

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

Why isn't this data getting sent into Redux?

I'm new to Redux and I think I'm starting to understand how it all works, but I'm having initial problems getting data into the Store.
I believe I'm close, but there's just something that I'm not getting. Any help is appreciated!
The reason I need this to work is because I have other components that will work with the same data, so I figured it's best to keep the data in the Redux Store. If there are other ways to solve this, please enlighten me.
Action:
import fetch from "isomorphic-fetch";
export const LOAD_DATA = "LOAD_DATA";
function getApiUrl() {
return `${window.appDefaultState.url.baseUrl}/api`;
}
export function loadStoresData() {
return function(dispatch) {
dispatch({
type: LOAD_DATA,
stores: data
});
fetch(
getApiUrl(),
{
method: "post",
credentials: 'same-origin',
body: JSON.stringify({
form_key: window.appDefaultState.formKey,
"cms/stores": 1
})
}
)
.then(response => response.json())
.then(json => {
console.log("fetched data in actions")
let data = json["cms/stores"];
console.log(data);
dispatch({
type: LOAD_DATA,
stores: data
});
})
.catch(e => {
console.log(e)
});
}
}
function getSuccess(data) {
console.log("getSuccess worked")
return (
type: LOAD_DATA,
stores: data
)
}
Reducer:
import {
LOAD_DATA
} from "actions/storelist.js";
function initialState() {
return Object.assign({}, {
stores: {},
}, window.appDefaultState.storeList);
}
export default function storeList(state, action) {
if (!state) {
state = initialState();
}
switch (action.type) {
case LOAD_DATA:
return Object.assign({}, state, {
stores: action.data
});
break;
}
return state;
}
Component (relevant parts):
import { connect } from "react-redux";
import { loadStoresData } from "actions/storelist.js";
const actions = {
loadStoresData
}
const mapStateToProps = (state, ownProps) => {
return Object.assign({
stores: state.stores
}, ownProps);
};
export default connect(mapStateToProps, actions)(StorePage);
You dispatch data in stores field, so it should be
switch (action.type) {
case LOAD_DATA:
return Object.assign({}, state, {
stores: action.stores
});
}
Replace action.data with action.stores

Categories