How do I fetch async data using dispatch in react js - reactjs

I have write following function in react js view recordings.js
fetchRecordingJson(file_name)
{
const { dispatch, history } = this.props;
if(dispatch)
{
dispatch(fetchrecordingJson(file_name));
history.push('/app/recording-analysis');
}
}
I want to travel data coming from using function fetchrecordingJson(file_name) to another page recording-analysis
Query Is:
How do I see the data coming from function fetchrecordingJson(file_name) in recording-analysis page
I am using redux-thunk library for async calls and below is my reducer code
case 'RECEIVE_JSON':
let newState = {data: action.data.data, info: action.data.info, count: state.count};
newState.annotations = action.data.annotations.length === 0 ? [[]] : action.data.annotations || [[]];
newState.file_name = action.file_name;
return Object.assign({}, newState);
below is my action.js code
export function receiveJSON(json, file_name) {
return {
type: RECEIVE_JSON,
file_name,
data: json
}
}
export function fetchRecordingJson(file_name) {
return dispatch => {
return axios.get(API_URL+`fetchjson/${file_name}`)
.then(json => {
dispatch(receiveJSON(json.data, file_name))
})
}
}

You can get the data saved in the redux store by using the mapStateToProps property of redux. I have created a sample component in which we are getting the newState value that you have just stored in the redux store in componentDidMount by calling this.props.data.
import React, { Component } from 'react'
import { connect } from 'react-redux'
class TestOne extends Component {
componentDidMount = () => {
console.log(this.props.data)
}
render() {
return (
<>
</>
)
}
}
const mapStateToProps = state => {
return {
data: state.newState
}
}
export default connect( mapStateToProps )(TestOne)

Related

React: Getting Initial State in Class based compopnents

I have a react-native, redux app, and after upgrading I've started getting some warnings about lifecycle hooks. My code looks like below:
import React from 'react';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { selectPosts} from '../redux/selectors/postSelector';
import { getPosts } from '../redux/actions/postActions';
class BasicScreen extends React.Component {
state = {
data: [],
myItems: [],
};
componentWillMount() {
this.getPosts();
}
componentDidMount() {
this.checkforItems();
}
getPosts = async () => {
// Call to a redux action
await this.props.getPosts();
};
checkforItems = async () => {
// myItems in initial state are set from data in
AsyncStorage.getItem('MyItems').then(item => {
if (item) {
this.setState({
myItems: JSON.parse(item),
});
} else {
console.log('No data.');
}
});
};
componentWillReceiveProps(nextProps) {
// Data comes from the redux action.
if (
nextProps.data &&
!this.state.data.length &&
nextProps.data.length !== 0
) {
this.setState({
data: nextProps.data,
});
}
}
render() {
return (
<View>/* A detailed view */</View>
)
}
}
const mapStateToProps = createStructuredSelector({
data: selectPosts,
});
const mapDispatchToProps = dispatch => ({
dispatch,
getPosts: () => dispatch(getPosts()),
});
export default connect(mapStateToProps, mapDispatchToProps)(BasicScreen);
To summarize, I was calling a redux action (this.getPosts()) from componentWillMount(), and then updating the state by props received in componentWillReceiveProps. Now both these are deprecated, and I am getting warnings that these are deprecated.
Apart from this, I am also setting some initial state by pulling some data from storage (this.checkforItems()). This gives me another warning - Cannot update a component from inside the function body of a different component.
To me it looks like the solution lies in converting this into a functional component, however, I'm stuck at how I will call my initial redux action to set the initial state.
UPDATE:
I converted this into a functional component, and the code looks as follows:
import React, { Fragment, useState, useEffect } from 'react';
import { connect } from 'react-redux';
import AsyncStorage from '#react-native-community/async-storage';
import { StyleSheet,
ScrollView,
View,
} from 'react-native';
import {
Text,
Right,
} from 'native-base';
import { createStructuredSelector } from 'reselect';
import {
makeSelectPosts,
} from '../redux/selectors/postSelector';
import { getPosts } from '../redux/actions/postActions';
const BasicScreen = ({ data, getPosts }) => {
const [myData, setData] = useState([]);
const [myItems, setItems] = useState([]);
const checkForItems = () => {
var storageItems = AsyncStorage.getItem("MyItems").then((item) => {
if (item) {
return JSON.parse(item);
}
});
setItems(storageItems);
};
useEffect(() => {
async function getItems() {
await getPosts(); // Redux action to get posts
await checkForItems(); // calling function to get data from storage
setData(data);
}
getItems();
}, [data]);
return (
<View>
<>
<Text>{JSON.stringify(myItems)}</Text>
<Text>{JSON.stringify(myData)}</Text>
</>
</View>
);
}
const mapStateToProps = createStructuredSelector({
data: makeSelectPosts,
});
const mapDispatchToProps = dispatch => ({
dispatch,
getPosts: () => dispatch(getPosts()),
});
export default connect(mapStateToProps, mapDispatchToProps)(BasicScreen);
It works, but the problem is that the first Text - {JSON.stringify(myItems)} - it is rerendering continuously. This data is actually got using checkForItems(). I wanted the useEffect to be called again only when the data updates, but instead something else is happening.
Also, I noticed that setData is not being called correctly. The data becomes available through the prop (data), but not from the state (myData). myData just returns empty array.

Redux State null in render method, even after redux state is updated correctly

I am in learning phase of react, and creating small application which fetches user wishlist from firebase table and updated redux store and I am trying to access that redux store in render method but when i console.log this.props.wishlist in render method its shows null. Redux state is updated correctly. Checked with redx dev tool.
redux state screenshot
Action creator which gets wishlist data from firebase API
export const fetchWishlist = (email)=> {
return dispatch => {
dispatch(fetchWishlistStart());
let rawMovieId=[];
let uniqueMovieIdList = [];
const queryParams ='?orderBy="email"&equalTo="'+email+'"';
axios.get('https://movie-project-6fc34.firebaseio.com/wishlist.json'+queryParams)
.then (response=>{
for(let key in response.data){
rawMovieId.push(response.data[key].movieId)
}
uniqueMovieIdList = [ ...new Set(rawMovieId) ];
dispatch(fetchMovieDetailsForWishlist(uniqueMovieIdList))
})
.catch(error=> {
console.log(error);
})
}
}
export const setMovieDetailsForWishlist = (movieDetailsList)=> {
return {
type:actionType.SET_MOVIEDETAILS_WISHLIST,
movieDetailsList:movieDetailsList
}
}
export const fetchMovieDetailsForWishlist = (movieList) => {
return dispatch => {
dispatch(fetchWishlistSuccess());
let updatedMovieList = []
movieList.map((currItem)=>{
let final_api_url = api_url+movieDetails_api_end_point+currItem+api_key+'&language='+language
axios.get(final_api_url)
.then(response=>{
updatedMovieList.push({
title:response.data.title,
movieId:response.data.id,
poster:response.data.poster_path
})
})
.catch(error=>{
console.log(JSON.stringify(error));
})
})
dispatch(setMovieDetailsForWishlist(updatedMovieList));
}
}
WhislistReducer --
import * as actionType from '../actions/actionType.js'
const intialState = {
wishList:null,
showLoader:false
}
const wishListReducer = (state=intialState, action) => {
switch (action.type) {
case actionType.FETCH_WISHLIST_START:
return {
...state,
showLoader:true
}
case actionType.FETCH_WISHLIST_SUCCESS:
return {
...state,
showLoader:false
}
case actionType.SET_MOVIEDETAILS_WISHLIST:
return {
...state,
showLoader:false,
wishList:action.movieDetailsList
}
default:
return state
}
}
export default wishListReducer;
wishlist component
import React, { Component } from 'react';
import {connect} from 'react-redux';
import {withRouter} from 'react-router-dom';
import * as action from '../store/actions/index'
export class Wishlist extends Component {
componentDidMount() {
this.props.fetchWishlist(window.localStorage.getItem('email'));
render() {
let wishListPageContent = '<div> Loading........</div>'
let userWishlistDetails = this.props.wishlist
console.log(userWishlistDetails);
if (!this.props.showLoader) {
wishListPageContent = (
<div> wishlist component</div>
)
}
return (
<div>
{wishListPageContent}
</div>
);
}
}
const mapStateToProps = state => {
return {
userEmail:state.authState.userEmail,
wishlist:state.wishlistState.wishList,
isAuthSuccess:state.authState.isAuthSuccess,
showLoader:state.wishlistState.showLoader
}
}
const mapDispatchToProps = dispatch => {
return {
fetchWishlist:(email)=>dispatch(action.fetchWishlist(email)),
fetchMovieDetailsForWishlist:(movieList)=>dispatch(action.fetchMovieDetailsForWishlist(movieList))
}
}
export default withRouter(connect(mapStateToProps,mapDispatchToProps)(Wishlist));

How to access redux-store from within react's componentDIdMount()

In the following code I am trying to pass the state.userData.userDetails from the redux-store to getleftpaneProductCatalogue(), but state.userData.userDetails is unaccessible to componentDidMount(). I tried assigning the state.userData.userDetails to this.prop.userProfile, but still this.prop.userProfile is an empty value. How to access the prop within componentDidMount?
import React,{Component} from 'react';
import { connect } from 'react-redux';
import {Row, Col } from 'react-materialize';
import {getleftpaneProductCatalogue} from '../actions/leftpane-actions';
import ProductCatalogueLeftPaneComp from '../components/pages/product-catalogue-leftpane';
class ProductCatalogueLeftPane extends Component {
constructor(props) {
super(props)
}
componentDidMount() {
console.log('this.props^', JSON.stringify(this.props));
this.props.getleftpaneProductCatalogue().then((data) => {
console.log('productdata', data);
})
}
render() {
return (
<div>
{JSON.stringify(this.props.userProfile)}
</div>
)
}
}
const mapStateToProps = (state) => {
console.log('state^', JSON.stringify(state));
return {leftpaneProductCatalogue: state.leftpaneProductCatalogue, userProfile: state.userData.userDetails};
};
const mapDispatchToProps = (dispatch) => {
return {
getleftpaneProductCatalogue: () => dispatch(getleftpaneProductCatalogue()),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(ProductCatalogueLeftPane);
You can access the state directly in mapDispatchToProps and pass it to getleftpaneProductCatalogue:
componentDidMount() {
const { dispatch, getleftpaneProductCatalogue }
dispatch(getleftpaneProductCatalogue())
}
const mapDispatchToProps = dispatch => {
return {
getleftpaneProductCatalogue: () => (dispatch, getState) => {
const state = getState()
const details = state.userData.userDetails
return dispatch(getleftpaneProductCatalogue(details))
},
dispatch
}
}
However, the way you're doing it, passing the state via mapStateToProps is still valid, but more verbose. Therefore the problem would be somewhere else.
Here's my bet. I guess you're getting the userData somewhere in your code with async API call and it's not being fetched yet. If that's the case - then you should wait for data being fetched firstly, then you can access it in your component ProductCatalogueLeftPane.

react-lifecycle-component have props in componentDidMount

I'm using react-lifecycle-component in my react app, and incurred in this situation where I need the componentDidMount callback to load some data from the backend. To know what to load I need the props, and I can't find a way to retrieve them.
here's my container component:
import { connectWithLifecycle } from "react-lifecycle-component";
import inspect from "../../../libs/inspect";
import fetchItem from "../actions/itemActions";
import ItemDetails from "../components/ItemDetails";
const componentDidMount = () => {
return fetchItem(props.match.params.number);
};
// Which part of the Redux global state does our component want to receive as props?
const mapStateToProps = (state, props) => {
return {
item: state.item,
user_location: state.user_location
};
};
// const actions = Object.assign(locationActions, lifecycleMethods);
export default connectWithLifecycle(mapStateToProps, { componentDidMount })(
ItemDetails
);
Any clues?
thanks.
import React, { Component } from 'react'
import { connect } from 'react-redux'
import fetchItem from '../actions/itemActions'
class Container extends Component {
state = {
items: []
}
componentDidMount() {
const { match } = this.props
fetchItem(match.params.number)
// if your fetchItem returns a promise
.then(response => this.setState({items: response.items}))
}
render() {
const { items } = this.state
return (
<div>
{ items.length === 0 ? <h2>Loading Items</h2> :
items.map((item, i) => (
<ul key={i}>item</ul>
))
}
</div>
)
}
const mapStateToProps = (state, props) => {
return {
item: state.item,
user_location: state.user_location
}
}
export default connect(mapStateToProps)(Container)
Though I don't see where you are using the props you take from your Redux store...

React Redux - How to dispatch an action on componentDidMount when using mapDispatchToProps in a connected component

I'm having problems with this. I'm creating a small app with react redux.
In the code below is my app.js component. It was working fine until I tried to use the mapDispatchToProps function inside connect. The problem is that I cannot invoke the dispatch action on componentDidMount anymore. The actions that were in componentDidMount and that now are on mapStateToProps need to be called on comoponentDidMount. Any clues in how to do that?
import React, { Component } from 'react';
import './App.css';
import '../../node_modules/bootstrap/less/bootstrap.less';
import { Route } from 'react-router-dom'
import * as ReadableAPI from '../ReadableAPI'
import HeaderNavigation from './HeaderNavigation';
import TableBody from './TableBody';
import { connect } from 'react-redux';
import sortAsc from 'sort-asc';
import sortDesc from 'sort-desc';
import {
selectedCategory,
fetchCategoriesIfNeeded,
fetchPostsIfNeeded,
invalidateSubreddit,
orderPost
} from '../actions'
class App extends Component {
state = {
posts: []
}
componentDidMount() {
const { dispatch, selectedCategory, fetchCategories, fetchPosts} = this.props
//dispatch(fetchCategoriesIfNeeded(selectedCategory))
//dispatch(fetchPostsIfNeeded(selectedCategory))
}
orderByScoreAsc = (posts) => {
return posts.sort(sortAsc('voteScore'))
}
orderByScoreDesc = (posts) => {
return posts.sort(sortDesc('voteScore'))
}
render() {
const { navCategories, posts } = this.props
return (
<div>
<HeaderNavigation navCategories = {navCategories} />
<Route exact path="/" render={()=>(
<TableBody
showingPosts={posts}
/>)}
/>
</div>
);
}
}
function mapStateToProps ( state ) {
const { categories, posts } = state
return {
navCategories: categories.items,
posts: posts.items
}
}
function mapDispatchToProps (dispatch) {
return {
changeOrder: (data) => dispatch(orderPost(data)),
fetchCategories: (data) => dispatch(fetchCategoriesIfNeeded(data)),
fetchPosts: (data) => dispatch(fetchPostsIfNeeded(data))
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(App)
I modified your code to what I think will work. I also left comments.
class App extends Component {
state = {
posts: []
}
componentDidMount() {
// no need to use dispatch again. Your action creators are already bound by
// mapDispatchToProps. Notice also that they come from props
const { selectedCategory, fetchCategoriesIfNeeded, fetchPostsIfNeeded} = this.props;
fetchCategoriesIfNeeded(selectedCategory);
fetchPostsIfNeeded(selectedCategory);
}
//... the same
}
function mapStateToProps ( state ) {
//... the same
}
function mapDispatchToProps (dispatch) {
// when arguments match, you can pass configuration object, which will
// wrap your actions creators with dispatch automatically.
return {
orderPost,
fetchCategoriesIfNeeded,
fetchPostsIfNeeded,
}
}
In map to dispatch you have fetchCategories/fetchPosts so therefore you need to call them like this:
componentDidMount() {
const { dispatch, selectedCategory, fetchCategories, fetchPosts } = this.props
//Call like this instead of fetchCategoriesIfNeeded/fetchPostsIfneeded
//dispatch(fetchCategories(selectedCategory))
//dispatch(fetchPosts(selectedCategory))
}
You have this:
function mapDispatchToProps (dispatch) {
return {
changeOrder: (data) => dispatch(orderPost(data)),
fetchCategories: (data) => dispatch(fetchCategoriesIfNeeded(data)),
fetchPosts: (data) => dispatch(fetchPostsIfNeeded(data))
}
}
So you need to call fetchCategories/fetchPosts from your props instead of fetchCatIfneeded/fetchPostsifneeded
You just don't. The mapDispatchToProps does exactly what you are trying to do in your component. Instead of calling a dispatch you call the method that was provided to your component by connect. in your case:
componentDidMount() {
const { selectedCategory, fetchCategories, fetchPosts} = this.props;
fetchCategories(selectedCategory);
fetchPosts(selectedCategory);
}

Resources