user3712353
user3712353

Reputation: 4199

React Redux table update row

I'm new to react and redux. I have a container which initialize a table component with a list of items, and onclick function. In the table component I have checkbox for each row. When I click the checkbox I want to select the row (change its style and add selected property to its element model). When I click on the checkbox I call the onclick property function, then find the item on the list by its id, and change its selected property. The view is not refreshing. I understand that a component is a "stupid" component that only binds the props and rendering.

What am I doing wrong?

// People container
<Table items={this.props.people} columns={this._columns} onRowSelect={this.selectRow} />

this.selectRow(id){
    const selectedLead =_.find(this.props.leads.docs, (lead)=>{
        return lead._id == id;
    })

    selectedLead.selected = !selectedLead.selected;
}

// Table Component - inside render()
{this.props.items.map((item, idx) => {
    console.log(item.selected);
    return <div style={styles.row(item.selected)}>etc...</div>
})}

Thanks :)

Upvotes: 4

Views: 4330

Answers (1)

Alex
Alex

Reputation: 2748

A React Component has props and state.

The difference is, that the Component will never change it props. But it can change it's state. This is why a Component will provide you the setState(...) Method, but no setProps(...) Method.

With that said, your approach to change the selected field in this.props is fundamentally not correct. (There also seems to be another problem in your code where you change the selected field in this.props.leads, but provide this.props.people to the table instead of this.props.leads)

Let me give you a basic example as to how I would solve your problem in Pure React (without a state library like Redux):

const Row = ({ item, onClick }) => (
    <tr style={styles.row(item.selected)} onClick={() => onClick(item.id)}>...</tr>
)

const Table = ({ items, onRowClick }) => (
    <table>
        {items.map(item => <Row item={item} onClick={onRowClick} />)}
    </table>
)

class PeopleTable extends React.PureComponent {
    constructor(props) {
        super(props)

        this.state = { people: props.people }
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.people !== this.state.people) {
            this.setState({ people: nextProps.people })
        }
    }

    setItemSelectedState(id) {
        this.setState((prevState) => {
            const people = prevState.people.map(item => ({
                ...item,
                selected: item.id === id ? !item.selected : item.selected,
            })

            return { people }
        })
    }

    handleRowClick = (id) => this.setItemSelectedState(id)

    render() {
        return (<Table items={people} onRowClick={this.handleRowClick} />)
    }
}

The things to notice here are:

  1. Row and Table are stateless components. They only take props and return jsx. Sometimes they are also referred to as presentational components.
  2. PeopleTable keeps track of the selected state of each item. This is why it needs state and must be a class.
  3. Because we can't change a components props, we have to keep a reference to props.people in this.state.
  4. componentWillReceiveProps makes sure that if our components receives another list of people, the state is updated accordingly.
  5. setItemSelectedState goes to the root of your problem. Instead of search and update of the item (like in your this.selectRow(id) method), we create a complete new list of people with map and call setState. setState will trigger a rerender of the component and because we created a new people list, we can use the !== check in componentWillReceiveProps to check if people has changed.

I hope this answer was helpful to your question.

Upvotes: 3

Related Questions