How to display react-redux state value without using setState? - javascript

This kind of problem has been answered before but I could not find a general way which works for all. Here is the state value screenshot:
The value came from node.js as
return res.send(available_balance);
Action was set as:
type: FETCH_DIGITAL_WALLET_BALANCE_BY_ID_SUCCESS,
digitalWalletUserAccountBalanceById: balance
As you can see the redux store has set the data right no doubt.
Now when I show the data as:
class UserUpdateModal extends React.Component {
constructor(props) {
...
}
render() {
return (
<div>
<h1>{this.props.initialValues.wallet_balance_by_id}</h1>
</div>
)
}
}
function mapStateToProps (state) {
return{
initialValues: {
wallet_balance_by_id: state.digitalWalletUserAccountBalanceById.data,
}
}
}
export default connect(
mapStateToProps,
)(withRouter(UserUpdateModal));
I get Error:
But when I create :
class UserUpdateModal extends React.Component{
this.state = {
wallet_balance_by_id:'',
}
this.getWallet_balance_by_id = this.getWallet_balance_by_id.bind(this);
}
getWallet_balance_by_id(){
this.state.viewWalletBalance==false?
this.setState({viewWalletBalance:true}):this.setState({viewWalletBalance:false})
}
....
}
Then call the function as input button it set the state and shows to the screen. So what is the basic way to shoe the redux state value to the screen without using a button to come around from the problem.
<h2>Balance</h2>
<h1>{ this.state.viewWalletBalance ?
this.props.initialValues.wallet_balance_by_id : null }</h1>
<input type="submit" value="Balance" onClick={this.getWallet_balance_by_id} />
{/* <h1>{this.props.initialValues.wallet_balance_by_id}</h1> */}

As you can see the redux store has set the data right no doubt.
The redux store gets the right value eventually but it doesn't always have the right value. Look again at the screenshot that you posted of Redux Dev Tools. Eventually the value is a number 10.12. But initially the value is an empty object {}. Why?
The problem is not in your component code or any of the code that you have included here. The problem is the initial state of your Redux store, which is setting the state.digitalWalletUserAccountBalanceById.data property to an empty object {}. Fix the initial state and your problems will go away. It should be a number or undefined.

From the screenshot, ( and mapStateToProps ) it seems like the initial value of wallet_balance_by_id is an Object ( which is state.digitalWalletUserAccountBalanceById.data ) which will throw that error because you can't render the Object like that, this is happening before the state update
{} => 10.12
It works when you click on the button because the value changes to a number and you can legally render it
update mapStateToProps to :
function mapStateToProps (state) {
return{
initialValues: {
wallet_balance_by_id: state.digitalWalletUserAccountBalanceById.data.wallet_balance_by_id,
// ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^
// this should not be an object
}
}
}
Or better :
function mapStateToProps (state) {
return{
initialValues: state.digitalWalletUserAccountBalanceById.data
}
}

Related

TypeError: Cannot read property '0' of undefined. React Redux App

I am getting this error in my code. I am able to read the correct state from my Redux Chrome extension though. I don't know where is the error coming from. I think it comes from.
TopTracks.propTypes = {
getHashParams: PropTypes.func.isRequired,
getMyTopTracks: PropTypes.func.isRequired,
myTopTracks: PropTypes.arrayOf(PropTypes.object),
}
const mapStateToProps = state => ({
myTopTracks: state.music.myTopTracks
})
Have I managed the mapStateToProps and PropTypes correctly? Because there is no sign of its object content: myTopTrackName and myTopTrackImage
Here is the full code:
class TopTracks extends Component{
componentWillMount(){
this.props.getMyTopTracks(getHashParams())
}
render(){
let link = `${ window.location.href }`
link = `/prueba/${link.substring(link.indexOf('#'))}`
return(
<div className="TopTracks">
Now Playing: { this.props.myTopTracks[0].myTopTrackName }
<img src={ this.props.myTopTracks[0].myTopTrackImage } alt="Top Track Album" style={{ width: 300 }} />
<Button href={ link }>Continue</Button>
</div>
)
}
}
TopTracks.propTypes = {
getHashParams: PropTypes.func.isRequired,
getMyTopTracks: PropTypes.func.isRequired,
myTopTracks: PropTypes.arrayOf(PropTypes.object),
}
const mapStateToProps = state => ({
myTopTracks: state.music.myTopTracks
})
export default connect(mapStateToProps, { getHashParams, getMyTopTracks } )( TopTracks )
If the error is coming from the code shown, it means that this.props.myTopTracks is undefined, which in turns means that state.music.myTopTracks is undefined.
The real problem is in your app's initial state. Look in your redux reducers and make sure that your initial state sets myTopTracks to an empty array, []. This is a common, safe way to initialize the app state before you fetch the real list from the server.
You'll also still need to change your view code to account for an empty list, because myTopTracks[0] will be undefined if the list is empty. One way to solve that is by creating a safe default first object in the list, which would be replaced by incoming data from the fetch, such as:
{
myTopTracks: [{
myTopTrackName: 'Loading...',
myTopTrackImage: ''
}]
}
Another method is to check the length of the list, and if it's empty, render something else, like a spinner. It really depends on the design requirements for your app at that point. But those are two options.

React with Redux: error on propType property

I'm new in the React world. I got a course to training React and Redux.
Like yesterday I got an error while I'm attending an online training
Even though, I walk through the author course and copy the code from the screen I get an error:
Warning: Failed propType: Required prop courses was not specified in CoursesPage. Check the render method of Connect(CoursesPage).
I have uploaded my code to github: https://github.com/tarcisiocorte/reactredux/blob/master/src/components/course/CoursesPage.js
again....I will appreciate some help.
import React, {PropTypes} from "react";
import {connect} from 'react-redux';
import * as courseActions from '../../actions/courseActions';
class CoursesPage extends React.Component {
constructor(props, context){
super(props, context);
this.state = {
course:{title: ""}
};
this.onTitleChange = this.onTitleChange.bind(this);
this.onClickSave = this.onClickSave.bind(this);
}
onTitleChange(event){
const course = this.state.course;
course.title = event.target.value;
this.setState({course: course});
}
courseRow(course, index){
return <div key={index}>{course.title}</div>;
}
onClickSave() {
this.props.dispatch(courseActions.createCourse(this.state.course));
}
render() {
return (
<div>
<h1>Courses</h1>
{this.props.courses.map(this.courseRow)}
<h1>Add Courses</h1>
<input
type="text"
onChange={this.onTitleChange}
value={this.state.course.title} />
<input
type="submit"
value="Save"
onClick={this.onClickSave} />
</div>
);
}
}
CoursesPage.propTypes = {
dispatch: PropTypes.func.isRequired,
courses: PropTypes.array.isRequired
};
function mapStateToProps(state, ownProps) {
return{
courses: state.courses
};
}
export default connect(mapStateToProps)(CoursesPage);
In https://github.com/tarcisiocorte/reactredux/blob/master/src/index.js#L11
You need to specify a default for courses.
You have specified that your courses prop is required:
courses: PropTypes.array.isRequired
so you need to pass in something from the redux store and by the looks of it the courses property in your redux store is undefined. (Put a breakpoint here to check that is actually the case)
You can either make sure your redux store always returns something for your courses or your can remove the isRequired constrain:
CoursesPage.propTypes = {
dispatch: PropTypes.func.isRequired,
courses: PropTypes.array
};
In your 'Routes' component, you'll want to change
<Route path="courses" component={CoursesPage} />
to
<Route path='courses' render={(stuff) => (
<CoursePage courses={stuff} />
)}/>
When you use component, you can't add your required props, so render would be a good alternative. This also means you'll have to add redux connections to your routes.js since you need to get that information from somewhere.
Another, more simpler, solution would be just to eliminate courses as a prop and get that information directly from redux when CoursePage loads up. You've already done half the battle with your mapStateToProps, therefore you dont need to have it with the "isRequired" in your propTypes. This is basically when Klugjo said, so if you decide to take this approach, give him credit.
I'd also hazard a guess that if 'courses' in your store doesn't exist, your isRequired is being triggered as well. So you might be able to keep isRequired as long as you have your data for that prop in the store.
For anyone coming across a similar failed prop type error, such as below, or if the other answers did not resolve your issue, the following might be an alternate fix for you. In the context of user428745's post above, someProjects and ResponsiblePage in the error below would correspond to the courses prop (some array of values) and the CoursesPage component, respectively.
Given user428745's setup below
CoursesPage.propTypes = {
dispatch: PropTypes.func.isRequired,
courses: PropTypes.array.isRequired
};
function mapStateToProps(state, ownProps) {
return {
courses: state.courses
};
}
The issue might be related to how the redux state gets the state.courses value in the first place. In my case, the prop (ie. courses as in state.courses) in mapStateToProps was being set before the data was available from the redux store. This happened due to an API data call that had not yet finished. My fix was:
function mapStateToProps(state, ownProps) {
return {
courses: state.courses || [] // Equivalent to statement below
//courses: state.courses == null ? [] : state.courses
};
}
If state.courses is null (due to API data not loaded yet) we return [] to satisfy the array requirement on our prop. If it is valid, which means the data was available and was put inside of state.courses, then we simply return state.courses similar to before.
Note also that there might be different required configuration setups (to make redux work properly), ie. depending on how you link your reducer(s) to your root reducer (which would be the content in index.js inside of reducers folder). If the error is still not fixed with these changes, try another approach with the root reducer, such as:
// From this (see `user428745`'s source files, where 'courseReducer' was imported as 'courses')
export default combineReducers({
courseReducer
});
// To this
export default combineReducers({
rootReducer: courseReducer
});
// Where 'mapStateToProps' would also have to change
function mapStateToProps(state, ownProps) {
return {
courses: state.rootReducer.courses || []
};
}
And where you intend to use this value, ie. with this.props.courses or props.courses in your CoursesPage setup, you could console log the values (or whatever you wanted to do) only when the array is not empty:
if (props.courses.length > 0) {
console.log(props.courses);
}
Or maybe listen to props.courses changes so that you perform something only "in the moment" after it changes (whereas the if statement above would be valid at all times, from when the prop was filled with values):
useEffect(() => {
if (props.courses.length > 0) {
console.log(props.courses);
}
}, [props.courses]);
Note that if you use useEffect, make sure it is within your CoursesPage component, and not in the "root" of the file where you would ie. write export default CoursesPage.

setState is not updating state instantly [duplicate]

This question already has answers here:
Why calling setState method doesn't mutate the state immediately?
(3 answers)
Closed 4 years ago.
I have simple component
class App extends Component {
handleClick() {
let banana = {message: 'banana triggered'};
this.setState({banana});
console.log(this); // banana is set in state!!!!
console.log(this.state); // state is null :O
setTimeout(() => {
console.log(this.state); // banana is set!
}, 5)
}
render() {
const {state, actions} = this.props;
return (
<div>
{this.state && this.state.banana.message} <br />
<button onClick={() => this.handleClick()}>Test</button>
{state.alert.message && <p>{state.alert.message}</p>}
<p onClick={() => actions.alert.success("This is not")}>
This is magic
</p>
</div>
)
};
}
export default connect(
state => (
{
state: {...state}
}
),
dispatch => (
{
actions: {
dispatch: dispatch,
alert: {
success: text => dispatch(alert.success(text))
}
}
}
)
)(App);
problem is what i need to add this.state && in my JSX rendering to check if this.state exists at all, i understand what in JavaScript it's normal, but is not normal in React.js? Should he react to state change instantly? Also what get me confused, is what from two console.logs, first (this) have banana set in state, and second one is empty. How?
Image below:
p.s. there is no such problem with Redux, only local component state
react's docs mention that state updates are asynchronous.
In order to act based on the change of the state, react setState function provides a callback which you can use as follows:
this.setState({banana}, () => {
console.log(this.state);
});
In regards to your comment, the value of the state didn't actually exist when it was printed. the value was calculated only after you clicked the expand arrow in the console see this for more deatils
According to react docs, setState() is asynchronous, and multiple calls during the same cycle may be batched together.
If you check the updated state value, you can add a callback method
this.setState({ banana }, ()=> {
// console.log(this.state);
// Here's the updated state
});
In your case, the first console.log(this) doesn't set the banana. See your code in Sandbox. It looks like first two console logs don't show any state as the initial state is null and after the timeout when the asynchronous call has finished it set the state with banana.

Component is not receiving current, but previous input values

I have two components to represent a list of articles and a filtering form. Every time any form field is changed, I need to send a HTTP request including the selected filters.
I have the following code for the SearchForm:
import React from 'react';
import { reduxForm, Field } from 'redux-form';
const SearchForm = ({ onFormChange }) => (
<form>
<Field component='select' name='status' onChange={onFormChange}>
<option>All</option>
<option value='published'>Published</option>
<option value='draft'>Draft</option>
</Field>
<Field
component='input'
type='text'
placeholder='Containing'
onChange={onFormChange}
/>
</form>
);
export default reduxForm({ form: 'myCustomForm' })(SearchForm);
And the following for the PostsList:
import React, { Component } from 'react';
import SearchForm from './SearchForm';
import { dispatch } from 'redux';
class PostsList extends Component {
constructor(props) {
super();
this.onFormChange = this.onFormChange.bind(this);
}
onFormChange() {
// Here I need to make the HTTP Call.
console.info(this.props.myCustomForm.values);
}
componentWillMount() {
this.props.actions.fetchArticles();
}
render() {
return (
<div>
<SearchForm onFormChange={this.onFormChange} />
<ul>
{ this.props.articles.map((article) => (<li>{article.title}</li>)) }
</ul>
</div>
);
}
}
const mapStateToProps = (state) => ({
myCustomForm: state.form.myCustomForm
});
const mapDispatchToProps = (dispatch) => ({
actions: {
fetchArticles: dispatch({ type: 'FETCH_ARTICLES' })
}
});
export default connect(mapStateToProps, mapDispatchToProps)(PostsList);
Though there is nothing going wrong with the rendering itself, something very awkful is happending with the myCustomForm.values prop when I change the form.
When I do that for the first time, the console.log(this.props.myCustomForm.values) call returns undefined, and the next calls return the previous value.
For example:
I load the page and select the draft option. undefined is printed.
I select published. { status: 'draft' } is printed.
I select draft again... { status: 'published' } is printed.
I inspected the redux store and the componend props. Both change according to the form interaction. But my function is returning the previous, not the new value sent by onChange.
This is clearly a problem with my code, most probably with the way I'm passing the function from parent to child component.
What am I doing wrong?
There is nothing wrong with your function. What I think is happening is that first time you select the option your callback is fired and is console logging current state for myCustomForm.values which haven't been yet changed by redux-form. So when the select changes:
your callback is fired...
...then redux-form is updating the state.
So. when your callback is making console.log it's printing not yet updated store.
do this, and you will see it's true:
onFormChange(e) {
// Here I need to make the HTTP Call.
console.info(e.currentTarget.value);
}
EDIT
My first question would be, do you really need to store this value in redux and use redux-form? It's a simple case, and you get current value in a way I showed you above.
However, if that's not the case, the callback is not required here, you just need to detect in your connected component (PostsList) that values have been changed in a form. You can achieve it with componentWillReceiveProps hook.
class PostsList extends Component {
constructor(props) {
super(props); // you should pass props to parent constructor
this.onFormChange = this.onFormChange.bind(this);
}
componentWillReceiveProps(nextProps) {
if(this.props.myCustomForm.values !== nextProps.myCustomForm.values) {
// do your ajax here....
}
}
componentWillMount(nextProps) {
this.props.actions.fetchArticles();
}
render() {
return (
<div>
<SearchForm />
<ul>
{ this.props.articles.map((article) => (<li>{article.title}</li>)) }
</ul>
</div>
);
}
}

Strange behaviour with Material-UI Text Field and Redux state trying to set default value?

I am trying to set a defaultValue property for a Text Field by getting the value from my Redux state however it is not updating accordingly.
I have passed the value as a prop from the container component down to my edit component like so:
render() {
const {data} = this.props
return (
<editcomponent value={this.props.data.value}
)
}
const mapStateToProps = (state) => {
return {
data: state.dataReducer
}
}
In my edit component I tried to just display it first and this works fine:
render() {
return (
<h3>this.props.value</h3>
)
}
When I reload the page with new data in my Redux state it updates accordingly. However, when I try the exact same thing except with a Text Field in which I am setting defaultValue it does not update.
This doesn't work:
render() {
return (
<TextField id="textfield_id" defaultValue={this.props.value}/>
)
}
It will work the initial time, and then when I reload the page with new data it doesn't set the defaultValue to the new data it stays the same as it originally was. If I use value instead then it will change the data but it won't allow me to edit the text box value anymore.
How can I solve this? I want to be able to have a defaultValue set by my Redux state and allow the value to be changed as the user changes/deletes what's in the text box.
I've the same problem and I fixed by following: https://stackoverflow.com/a/36959225/842097
Don't use defaultValue, but set initialValues in the state
initialValues: {
name: theName
}
I have faced this bug as well, solved by mapping the props to a state first and then using that state instead with value prop.
function updateValue(e) {
this.setState({ value: e.target.value });
}
render {
return (
<TextField id="textfield_id" value={this.state.value} onChange={this.updateValue}/>
);
}

Categories