facebookarchive / fixed-data-table

A React table component designed to allow presenting thousands of rows of data.
http://facebook.github.io/fixed-data-table/
Other
4.3k stars 552 forks source link

Checkbox in each row using a Redux store not rendering correctly #476

Open szszoke opened 7 years ago

szszoke commented 7 years ago

Hi,

I've been trying to create a table which has a checkbox in each row and also one in the header.

This is the desired behavior: a. Header checkbox toggles the checkboxes in the rows - this works b. Individual chechboxes can be checked/unchecked - this doesn't works

I have a Redux store where I have a boolean field for the header checkbox and an array of objects each having a boolean field for the checkbox in the row.

In my example code, if I click a checkbox at a row, the correct action get fired, the state gets updated but the table is not updated. The strange thing is that the checkbox in the header can toggle the checkboxes in the rows without problem.

Am I missing something when I set the value for the checkboxes for rows or this is a bug?

Here is a complete example in typescript:

import * as React from "react";
import * as ReactDOM from "react-dom";

import {
    createStore,
    applyMiddleware,
} from "redux";

import { connect, Provider } from "react-redux";

import { Table, Column, Cell } from "fixed-data-table";

import "fixed-data-table/dist/fixed-data-table.min.css";

const createLogger = require("redux-logger");

const middlewares = applyMiddleware(createLogger({ colapsed: true }));

interface IData {
    checked: boolean;
    id: number;
    value: string;
};

interface IState {
    allChecked: boolean;
    data: IData[];
};

const INITIAL_STATE: IState = {
    allChecked: false,
    data: [
        {
            checked: false,
            id: 1,
            value: "One"
        },
        {
            checked: false,
            id: 2,
            value: "Two"
        },
        {
            checked: true,
            id: 3,
            value: "Three"
        },
        {
            checked: false,
            id: 4,
            value: "Four"
        },
        {
            checked: false,
            id: 5,
            value: "Five"
        }
    ]
};

const CHECK = "CHECK";
const CHECK_ALL = "CHECK_ALL";

function check(checked: boolean, index: number) {
    return {
        type: CHECK,
        payload: {
            checked: checked,
            index: index
        }
    };
}

function checkAll(checked: boolean) {
    return {
        type: CHECK_ALL,
        payload: {
            checked: checked
        }
    };
}

function rootReducer(state: IState = INITIAL_STATE, action: any): IState {
    switch (action.type) {
        case CHECK: {
            const {
                checked,
                index
            } = action.payload;

            state.data[index].checked = checked;

            return {
                ...state,
                allChecked: false
            };
        }

        case CHECK_ALL: {
            const { checked } = action.payload;

            state.data.forEach(d => {
                d.checked = checked;
            });

            return {
                ...state,
                allChecked: checked
            };
        }

        default: {
            return state;
        }
    }
}

const store = createStore<IState>(rootReducer, INITIAL_STATE, middlewares);

interface IAppProps extends React.Props<any> {
    allChecked: boolean;
    data: IData[];
    onCheck: (checked: boolean, index: number) => void;
    onCheckAll: (checked: boolean) => void;
};

class App extends React.Component<IAppProps, void> {
    handleAllChecked(event) {
        const {
            onCheckAll
        } = this.props;

        onCheckAll(event.target.checked);
    }

    handleChecked(event, index) {
        const {
            onCheck
        } = this.props;

        onCheck(event.target.checked, index);
    }

    render() {
        const {
            allChecked,
            data
        } = this.props;

        return (
            <Table
                rowsCount={data.length}
                rowHeight={30}
                headerHeight={50}
                width={800}
                height={600}>
                <Column
                    width={100}
                    header={props => (
                        <Cell>
                            <label>
                                <input type="checkbox" checked={allChecked} onChange={event => this.handleAllChecked(event)} />
                                All
                            </label>
                        </Cell>
                    )}
                    cell={({rowIndex, ...props}) => (
                        <Cell>
                            <input type="checkbox" checked={data[rowIndex].checked} onChange={event => this.handleChecked(event, rowIndex)} />
                        </Cell>
                    )} />
                <Column
                    width={100}
                    header={
                        <Cell>
                            Id
                        </Cell>
                    }
                    cell={({rowIndex, ...props}) => (
                        <Cell>
                            {data[rowIndex].id}
                        </Cell>
                    )} />
                <Column
                    width={100}
                    header={
                        <Cell>
                            Value
                        </Cell>
                    }
                    cell={({rowIndex, ...props}) => (
                        <Cell>
                            {data[rowIndex].value}
                        </Cell>
                    )} />
            </Table>
        );
    }
}

function mapPropsToState(state: IState) {
    return {
        allChecked: state.allChecked,
        data: state.data
    };
}

function mapDispatchToState(dispatch: any) {
    return {
        onCheck: (checked: boolean, index: number) => dispatch(check(checked, index)),
        onCheckAll: (checked: boolean) => dispatch(checkAll(checked))
    };
}

const AppComponent = connect(
    mapPropsToState,
    mapDispatchToState
)(App);

ReactDOM.render(
    <Provider store={store}>
        <AppComponent />
    </Provider>,
    document.getElementById("app")
);