父组件连接到redux商店,但子组件不会在状态更改时重新发送

时间:2015-11-29 15:27:15

标签: reactjs redux

当我在redux商店中更改预算对象的数据时,负责呈现该对象的组件在数据更改时不会重新呈现该组件。

我有一个<Budget />组件,它会将预算对象包含在属于我的州/商店的预算对象数组中。我的商店有以下形状:

departmentBudgets  : {
    isFetching : false,
    budgets    : [ {
        department : 'String',
        owner      : {
            name  : 'String',
            email : 'String',
            title : 'String'
        },
        budget     : 'Number'
    } ]
}

我希望<Budget />组件在编辑其中一个属性时重新呈现和更新预算对象的显示。我正在处理的功能是允许用户编辑和更新预算属性。我的redux减速机如下所示:

import * as types from '../constants/actionTypes';
import union from 'lodash/array/union';
import uniq from 'lodash/array/uniq';

const initialState = {
    isFetching : false,
    budgets    : []
};

export default function budgets( state = initialState, action = {} ) {
    const { type, payload, meta } = action;

    switch ( type ) {
        case types.FETCH_CURRENT_BUDGETS:
            return Object.assign({}, state, {
                isFetching : true
            });
        case types.RECEIVE_CURRENT_BUDGETS:
            return Object.assign({}, state, {
                isFetching : false,
                budgets    : uniq(union(state.budgets, payload), '_id')
            });
        case types.UPDATE_DEPARTMENT_BUDGET:
            const changedIndex = state.budgets.findIndex(( budget ) => {
                return budget._id === payload._id;
            });
            const newBudgets = [ ...state.budgets.slice(0, changedIndex),
                Object.assign({}, state.budgets[ changedIndex ], {
                    budget : payload.budget
                }),
                ...state.budgets.slice(changedIndex + 1) ];
            return Object.assign({}, state, {
                budgets : newBudgets
            });
        default:
            return state;
    }
}

我是否在上面的reducer中正确处理了更新?我相信这不会改变状态对象,但也许不是吗?

我的反应组件树如下所示呈现此数据:

DepartmentBudgetsContainer
  DepartmentBudgets
    Budget

container元素从数据库中获取当前数据,并使用react-redux将其连接到商店,如下所示:

import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { fetchBudgets, updateBudget } from '../../actions/departmentBudget.actions';
import DepartmentBudgets from '../../components/administration/department_budgets/DepartmentBudgets';

const displayName = 'DepartmentBudgetContainer';
const propTypes = {
    budgets : PropTypes.object
};

class DepartmentBudgetContainer extends Component {
    constructor( props, context ) {
        super(props, context);
        this.budgetDidchange = this.budgetDidchange.bind(this);
    }

    componentDidMount() {
        let { dispatch } = this.props;
        dispatch(fetchBudgets());
    }

    budgetDidchange( id, budget ) {
        let { dispatch } = this.props;
        const newBudget = {
            _id    : id,
            budget : budget
        };
        dispatch(updateBudget(newBudget));
    }

    render() {
        const { budgets } = this.props;
        return (
            <DepartmentBudgets updateBudget={this.budgetDidchange} budgets={budgets.budgets} />
        );
    }
}

DepartmentBudgetContainer.displayName = displayName;
DepartmentBudgetContainer.propTypes = propTypes;

function mapStateToProps( state ) {
    return {
        budgets : state.departmentBudgets.budgets
    }
}

export default connect(mapStateToProps)(DepartmentBudgetContainer);

然后,DepartmentBudgets组件负责创建每个预算组件,如下所示:

import React, { Component, PropTypes } from 'react';
import TableComps from '../../../../node_modules/material-ui/lib/table';
import Budget from './Budget';

const Table = TableComps.Table;
const TableHeader = TableComps.TableHeader;
const TableRow = TableComps.TableRow;
const TableHeaderColumn = TableComps.TableHeaderColumn;
const TableBody = TableComps.TableBody;
const TableRowColumn = TableComps.TableRowColumn;

const displayName = 'DepartmentBudgets';
const propTypes = {
    budgets      : PropTypes.arrayOf(PropTypes.object).isRequired,
    updateBudget : PropTypes.func.isRequired
};

export default class DepartmentBudgets extends Component {
    constructor( props ) {
        super(props);     
    }

    render() {
        const { budgets, updateBudget } = this.props;

        const budgetsElements = budgets.map(( budget ) => {
            return <Budget updateBudget={updateBudget} key={budget._id} budget={budget} />
        });

        return (
            <Table>
                <TableHeader adjustForCheckbox={false}
                             displaySelectAll={false}>
                    <TableRow onCellClick={this.handleCellClick}>
                        <TableHeaderColumn>Department</TableHeaderColumn>
                        <TableHeaderColumn>Budget</TableHeaderColumn>
                        <TableHeaderColumn>Owner</TableHeaderColumn>
                    </TableRow>
                </TableHeader>
                <TableBody stripedRows={false}>
                    {budgetsElements}
                </TableBody>
            </Table>
        );
    }
}

DepartmentBudgets.displayName = displayName;
DepartmentBudgets.propTypes = propTypes;

最后,预算部分负责提供实际预算。我还想包括在线编辑预算的功能,该功能包含在此组件中。但是,当我编辑预算时,组件不会重新渲染:

import React, { Component, PropTypes } from 'react';
import Table from '../../../../node_modules/material-ui/lib/table';
import numeral from 'numeral';
import InlineEdit from 'react-edit-inline';

const TableRow = Table.TableRow;
const TableRowColumn = Table.TableRowColumn;

const displayName = 'Budget';
const propTypes = {
    budget       : PropTypes.object.isRequired
};

export default class Budget extends Component {
    constructor( props, context ) {
        super(props, context);
        console.log('Budget this:', this.props);
        this.handleBudgetChange = this.handleBudgetChange.bind(this);
    }

    validateBudget( num ) {
        return (Number(num) > 0 && Number(num) < 100 && !Number.isNaN(num));
    }

    handleBudgetChange( num ) {
        const inputNum = Number(num.budget) > 1 ? Number(num.budget) / 100 : Number(num.budget);
        const budgetId = this.props.budget._id;
        this.props.updateBudget(budgetId, inputNum);
    }

    render() {
        const cellStyle = {
            textAlign : 'center'
        };
        const editCellStyle = {
            cursor         : 'pointer',
            textDecoration : 'underline'
        };
        const { department, owner, budget } = this.props.budget;
        const editControl = (
            <InlineEdit
                validate={this.validateBudget}
                change={this.handleBudgetChange}
                text={numeral(this.props.budget.budget).format('0.0%')}
                paramName="budget"
                activeClassName="editing"
            />
        );

        return (
            <TableRow style={cellStyle}
                      displayBorder={false}>
                <TableRowColumn>{department}</TableRowColumn>
                <TableRowColumn style={editCellStyle}>{editControl}</TableRowColumn>
                <TableRowColumn>{owner.name}</TableRowColumn>
                <TableRowColumn style={editCellStyle}>Edit</TableRowColumn>
            </TableRow>
        );
    }
}

Budget.displayName = displayName;
Budget.propTypes = propTypes;

当我在线编辑预算时,状态的值会发生变化,但组件不会重新渲染它。

我是否应该将<Budget />组件连接到商店并在此处收听更改?

还是有更好的方法来解决我想要做的事情吗?

感谢您的帮助!

2 个答案:

答案 0 :(得分:2)

快速浏览一下,是不是你没有将正确的道具传递给你的儿童组件?对于容器中的DepartmentBudgets,您是否应该将预算映射到预算&#39;而不是&#39; budgets.budgets&#39;?

答案 1 :(得分:0)

您确定this.props.updateBudget(budgetId, inputNum);是否正确触发其父组件的功能?当它从容器传递下来时,你会想要做<DepartmentBudgets updateBudget={this.budgetDidchange.bind(this)} ... />之类的事情。我不相信构造函数绑定弥补了这一点,但你总是可以在函数中抛出console.log("test")来查看问题是否是函数本身而与Redux无关。

请参阅:https://facebook.github.io/react/tips/communicate-between-components.html