Reputation: 4199
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
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:
Row
and Table
are stateless components. They only take props
and return jsx. Sometimes they are also referred to as presentational components.PeopleTable
keeps track of the selected
state of each item. This is why it needs state
and must be a class
.props
, we have to keep a reference to props.people
in this.state
.componentWillReceiveProps
makes sure that if our components receives another list of people, the state is updated accordingly.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