I am just trying to learn to use redux, and I have a very simple counter list ponent which has a list of child counter ponents.
I have an onIncrement action on the counter, which I want to increment the count when clicked.
When I click the increment it updates the parent state, however the child counter does not update. If I browse away and then back to the list, it does render correctly, which seems to me to mean that the state has updated.
Here is my code:
Counter Component
import React, { Component } from "react";
import { connect } from 'react-redux';
import { incrementCounter } from '../../actions/counterActions';
import PropTypes from 'prop-types';
class Counter extends Component {
render() {
return <div className="m-2">
<b>{this.props.counter.count}</b>
<button className="btn btn btn-secondary btn-sm m-2" onClick={() => { this.onIncrement(this.props.counter) }}>Increment</button>
</div>;
}
onIncrement(counter) {
this.props.incrementCounter(counter);
}
}
const mapStateToProps = state => ({
})
Counter.propTypes = {
incrementCounter: PropTypes.func.isRequired,
}
export default connect(mapStateToProps, { incrementCounter })(Counter);
Counter List ponent
import React, { Component } from "react";
import { RouteComponentProps } from "react-router";
import { CounterContext } from "../../contexts/context.js";
import Counter from "./Counter";
import { NewItem } from "./NewItem";
import ItemContainer from "../layout/ItemContainer";
import { connect } from 'react-redux';
import { getCounters } from '../../actions/counterActions';
import PropTypes from 'prop-types';
class CounterList extends Component {
ponentWillMount() {
if (this.props.counters.length == 0) {
this.props.getCounters();
}
}
render() {
const counterItems = this.props.counters.map(counter => <Counter key={counter.id} counter={counter} />);
return <div>{ counterItems }</div>;
}
}
const mapStateToProps = state => ({
counters: state.counters.items
})
CounterList.propTypes = {
getCounters: PropTypes.func.isRequired,
counters: PropTypes.array.isRequired
}
export default connect(mapStateToProps, { getCounters })(CounterList);
Counter Actions
import { GET_COUNTERS, INCREMENT_COUNTERS } from '../actions/types';
export const getCounters = () => dispatch => {
const counters = [{ id: 1, count: 4 }, { id: 2, count: 3 }, { id: 3, count: 0 }];
// this could be API call to get initial counters
console.log('In GetCounters', GET_COUNTERS);
return dispatch({
type: GET_COUNTERS,
payload: counters
})
}
export const incrementCounter = (counter) => dispatch => {
// this could be API call to get initial counters
counter.count++;
return dispatch({
type: INCREMENT_COUNTERS,
payload: counter
})
}
Counter reducers
import { GET_COUNTERS, INCREMENT_COUNTERS } from '../actions/types';
const initialState = {
items: []
}
export default function (state = initialState, action) {
console.log(action.type);
switch (action.type){
case GET_COUNTERS:
return {
...state,
items: action.payload
};
case INCREMENT_COUNTERS:
var counter = action.payload;
const counters = [...state.items];
const index = counters.findIndex(x => x.id == counter.id);
counters[index] = counter;
return {
...state,
items: counters
};
default:
return state;
}
}
I am just trying to learn to use redux, and I have a very simple counter list ponent which has a list of child counter ponents.
I have an onIncrement action on the counter, which I want to increment the count when clicked.
When I click the increment it updates the parent state, however the child counter does not update. If I browse away and then back to the list, it does render correctly, which seems to me to mean that the state has updated.
Here is my code:
Counter Component
import React, { Component } from "react";
import { connect } from 'react-redux';
import { incrementCounter } from '../../actions/counterActions';
import PropTypes from 'prop-types';
class Counter extends Component {
render() {
return <div className="m-2">
<b>{this.props.counter.count}</b>
<button className="btn btn btn-secondary btn-sm m-2" onClick={() => { this.onIncrement(this.props.counter) }}>Increment</button>
</div>;
}
onIncrement(counter) {
this.props.incrementCounter(counter);
}
}
const mapStateToProps = state => ({
})
Counter.propTypes = {
incrementCounter: PropTypes.func.isRequired,
}
export default connect(mapStateToProps, { incrementCounter })(Counter);
Counter List ponent
import React, { Component } from "react";
import { RouteComponentProps } from "react-router";
import { CounterContext } from "../../contexts/context.js";
import Counter from "./Counter";
import { NewItem } from "./NewItem";
import ItemContainer from "../layout/ItemContainer";
import { connect } from 'react-redux';
import { getCounters } from '../../actions/counterActions';
import PropTypes from 'prop-types';
class CounterList extends Component {
ponentWillMount() {
if (this.props.counters.length == 0) {
this.props.getCounters();
}
}
render() {
const counterItems = this.props.counters.map(counter => <Counter key={counter.id} counter={counter} />);
return <div>{ counterItems }</div>;
}
}
const mapStateToProps = state => ({
counters: state.counters.items
})
CounterList.propTypes = {
getCounters: PropTypes.func.isRequired,
counters: PropTypes.array.isRequired
}
export default connect(mapStateToProps, { getCounters })(CounterList);
Counter Actions
import { GET_COUNTERS, INCREMENT_COUNTERS } from '../actions/types';
export const getCounters = () => dispatch => {
const counters = [{ id: 1, count: 4 }, { id: 2, count: 3 }, { id: 3, count: 0 }];
// this could be API call to get initial counters
console.log('In GetCounters', GET_COUNTERS);
return dispatch({
type: GET_COUNTERS,
payload: counters
})
}
export const incrementCounter = (counter) => dispatch => {
// this could be API call to get initial counters
counter.count++;
return dispatch({
type: INCREMENT_COUNTERS,
payload: counter
})
}
Counter reducers
import { GET_COUNTERS, INCREMENT_COUNTERS } from '../actions/types';
const initialState = {
items: []
}
export default function (state = initialState, action) {
console.log(action.type);
switch (action.type){
case GET_COUNTERS:
return {
...state,
items: action.payload
};
case INCREMENT_COUNTERS:
var counter = action.payload;
const counters = [...state.items];
const index = counters.findIndex(x => x.id == counter.id);
counters[index] = counter;
return {
...state,
items: counters
};
default:
return state;
}
}
Share
Improve this question
asked Aug 21, 2018 at 13:19
user3284707user3284707
3,3513 gold badges46 silver badges81 bronze badges
3 Answers
Reset to default 5I guess the problem could be that you are assigning the same old counter object with the updated value of count to your counters[index] and hence the Counter ponent doesn't see a change. This could be because shouldComponentUpdate does a shallow check and the object reference to the counter prop remains the same and your ponent doesn't re-render.
You should be using deep cloning if you are using multi-dimensional arrays or nested objects in your state.
Use libraries like Immutable.js is remended to make sure your reducers remain pure.
case INCREMENT_COUNTERS:
var counter = action.payload;
const counters = JSON.parse(JSON.stringify(state.items)); //this creates a deep copy
const index = counters.findIndex(x => x.id == counter.id);
counters[index].count = counter.count;
return {
...state,
items: counters
};
You're not mapping the state to props, for example:
function mapStateToProps = state => {
return { count: state.count };
}
This will map the state in your redux that reflects the change of the counter to the props of your ponent where you wanna display the current count. Hope this helps!
You could try put ponentWillReceiveProps
inside your Counter
ponent and check if you are getting the new counter
props every time you click the button.
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744966066a4603685.html
评论列表(0条)