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...
Related
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));
I'm trying to edit an object and replace it in array using React and Redux like this:
case EDIT_LANGUAGE:
let languages = [...state.languageSkills];
languages[languages.findIndex(el => el.id === action.payload.id)] = action.payload;
return {
...state,
languageSkills: languages
};
'languages' array looks find before return statement, but state is not re-rendered. I guess I'm mutating state somehow. Other actions (delete, get, set) are working fine. Any tips?
EDIT. This is relevant part of the component that should render
import { setLanguages, getLanguages } from '../../actions';
import {connect} from 'react-redux';
import {bindActionCreators} from "redux"
import React, { Component } from 'react';
class UserProfile extends Component {
constructor(props) {
super(props);
}
render() {
const languageSkillItems = this.props.languageSkills.map((languageSkill) => {
return (
<LanguageSkillItem key={languageSkill.id} item={languageSkill} />
)
});
return (
<div className="profile">
<Language languageSkillItems={languageSkillItems} />
</div>
)
}
}
const mapStateToProps = (state) => {
return {
languageSkills: state.languageSkills
};
};
const mapDispatchToProps = dispatch => {
return {
...bindActionCreators({ setLanguages, getLanguages }, dispatch)
}
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(UserProfile
);
You need to create a new array reference, easiest way is just to use map, like so:
case EDIT_LANGUAGE:
const languageSkills = state.languageSkills.map(el => {
if(el.id === action.payload.id) {
return action.payload;
}
return el;
});
return {
...state,
languageSkills
};
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.
I have a component which is using redux connect. In this component I have mapStateToProps which getting project from redux state and projectTransform is a value which has filter values from project redux state:
import React, { Component } from 'react';
import PropTypes from "prop-types";
import { connect } from 'react-redux';
class ProjectForm extends Component {
constructor(props){
super(props);
}
componentDidMount() {
const {
fetchProject,
} = this.props;
fetchProject();
}
onClick() {
this.setState({
project1: {
"a": 1,
"b": 2
}
})
}
render() {
const { project1 } = this.props;
return (
<div>
<button onClick={onClick()} />
</div>
)
}
}
ProjectForm.propTypes = {
fetchProject: PropTypes.func.isRequired
};
function mapDispatchToProps (dispatch) {
return
fetchProject: () => dispatch(projectActions.getProjectRequest()),
}
}
function mapStateToProps ( state ) {
const { project} = state
return {
project: project,
project1: ((project) => {
return project[0]
})(project)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ProjectForm)
I trying to now trigger re-rendering on the button but I have not clue how to do it as I tried.
this.setState((previousState) => {
project1: [JSON value from Form]
});
Also why previousState is null I would assume it would have mapStateToProps data.
Any idea how to do it without dispatching whole redux? Or how to do it in a proper way?
The problem was with reading data not from the state but props.
render() {
const { project1 } = this.state;
return (
<div>
<button onClick={onClick()} />
</div>
)
}
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);
}