1

I was trying to count the completed tasks, and the logic should be right I believe, but seems the this.setState({}) is not responding/reached at all, it's not doing anything to the data 'qtySelected' in the this.state({}) since the console.log('ts') didn't have a result in the console. Anyone knows why, maybe it's some react lifecycle issue?

export class Main extends Component {
    constructor(pros) {
        super(pros)
        this.state = {
            tasks: [
                {
                    id: 1,
                    content: "Sara's doctor and vaccine",
                    due: '2020-08-29',
                    completed: false
                },
                {
                    id: 2,
                    content: "Trash bags / facial masks / child allowance",
                    due: '2020-08-28',
                    completed: false
                },
                {
                    id: 3,
                    content: "Apply for Portugal nationality",
                    due: '2020-09-31',
                    completed: false
                },
                {
                    id: 4,
                    content: "My registeration card",
                    due: '2020-09-28',
                    completed: false
                },
                {
                    id: 5,
                    content: "contact ADEM",
                    due: '2020-12-31',
                    completed: false
                },
                {
                    id: 6,
                    content: "Pay loan",
                    due: '2020-09-03',
                    completed: true
                }
            ],
            qtySelected: 0
        }

    }


    componentDidMount() {    
        this.countSelected = () => {
            console.log('ts')
            let tasksCompleted = this.state.tasks.filter(task => {
                return task.completed === true
            })
            this.setState({
                qtySelected: tasksCompleted.length
            })
        }    
    }

    render() {
        return (
            <div>
                <table>
                    <tfoot>
                        <tr>
                            <td style={{ whiteSpace: "nowrap" }}>{this.state.qtySelected} selected</td>
                        </tr>
                    </tfoot>
                </table>
            </div>
        )
    }
}
Hai Na Zheng
  • 167
  • 3
  • 11

3 Answers3

2

You're not calling the this.countSelected function anywhere. Either way, data you can compute directly from the current state should not be stored in the state.

export class Main extends Component {
  constructor(props) {
    super(props);
    this.state = {
      tasks: [
        // ...
      ],
    };
  }

  render() {
    const selectedCount = this.state.tasks.filter((task) => task.completed).length;
    return (
      <div>
        <table>
          <tfoot>
            <tr>
              <td style={{ whiteSpace: "nowrap" }}>{selectedCount} selected</td>
            </tr>
          </tfoot>
        </table>
      </div>
    );
  }
}
AKX
  • 152,115
  • 15
  • 115
  • 172
  • thanks for the solution, it worked. But you said 'should not', why? It's just a bad practice but still works, or it's just wrong and will not work (I tried still keeping the 'qtySelected' state just like what Luke Storry suggested but it only worked when loaded and would not update when i checked other boxes, is it caused by me adding this extra new state qtySeleceted?) – Hai Na Zheng Sep 04 '20 at 11:58
  • It's just bad practise - it'll still work, but every time you use `setState` it will re-render the component - if you are just making new state based purely on old state, you don't need to make it re-render twice - just store the computed value in a variable as in this answer – Luke Storry Sep 04 '20 at 12:30
2

You are not actually running anything in the componentDidMount - you are just defining a new function this.countSelected.

Also using the callback version of setState is best practise when you are using a previous state value to update state.


All together, you can use this as your new componentDidMount and it should work as expected:

componentDidMount() {    
    this.setState(state => ({
        qtySelected: state.tasks.filter(task => task.completed).length
    }))
}

Alternatively, you probably should just use this computed value directly instead of forcing a second re-render, as it doesn't really need to be a separate state variable.

<td style={{ whiteSpace: "nowrap" }}>
  {this.state.tasks.filter(task => task.completed).length} selected
</td>
Luke Storry
  • 6,032
  • 1
  • 9
  • 22
  • thanks for your explanation, but the first way it still has a problem: it only worked one time when you just loaded the page, and afterwards if you check other checkboxes, the number doesn't update and stays at '1' selected. The second one works without problems though. – Hai Na Zheng Sep 04 '20 at 11:50
  • 1
    Yes, that is the exact definition of `componentDidMount`. It will run once, on mount. If you want it to run every render, put it in the render function. – Luke Storry Sep 04 '20 at 12:28
1

Nothing is executing this.countSelected function. So remove the function or make it extern to componentDidMount and call it from componentDidMount.

JB_DELR
  • 737
  • 1
  • 4
  • 7