简体   繁体   中英

React doesn't work as expected in working with two components file

I was following a tutorial for react and I need some help. In my full code I have 2 files .jsx . The men in the tutorial sad that if we have to increment our value in the state it's better to have one truth. So I've moved the increment code in the file with the original state, but I can't really increment the value. Here is my code of the first file:

import React, { Component } from 'react';

class Counter extends Component {

    render() {
        return (
        <div>
            <span className={this.getBadgeClasses()} >{this.formatCount()}</span>
            <button onClick={() => this.props.onIcrement(this.props)} className="btn btn-secondary btn-sm">Increment</button>
            <button onClick={() => this.props.onDelete(this.props.id)} className="btn btn-danger btn-sm m-2">Delete</button>
        </div>
        );
    }

    getBadgeClasses() {
        let classes = "badge m-2 badge-";
        classes += this.props.value === 0 ? "warning" : "primary";
        return classes;
    }

    formatCount() {
        const value = this.props.value;
        return value === 0 ? "Zero" : value;
        }
    }
    
    
    export default Counter;

Here is my code of the second file (the one with the state):

import React, { Component} from 'react';
import Counter from './counter.jsx'

class Counters extends Component {
    state = {
        counters: [
            { id: 1, value: 4},
            { id: 2, value: 0},
            { id: 3, value: 0},
            { id: 4, value: 0}
        ]
    };

    handleIncrement = (counter) => {
        const counters = [...this.state.counters];
        const index = counters.indexOf(counter);
        counters[index] = {...counters[index]};
        counters[index].value++;
        this.setState({ counters });
    }

    handleReset = () => {
    }

    handleDelete = (counterId) => {
        const counters = this.state.counters.filter(c => c.id !== counterId);
        this.setState({ counters });
    }

    render() {
        return (
            <div>
                <button onClick={this.handleReset} className="btn btn-primary btn-sm m-2">Reset</button>
                {this.state.counters.map(counter => <Counter key={counter.id} onIcrement={this.handleIncrement} onDelete={this.handleDelete} id={counter.id}  value={counter.value}/>)}
            </div>
        );
    }
}

export default Counters;

There are no errors in the code, just this problem with the increment that doesn't work as it should. Could you help me?

Nice work, Elder Patten Ferreira is very right

However you may want to consider Functional Component instead of Class Component. Everything a class component does now you can achieve with functional component with hooks.

so you can have your Counter.jsx as below:

import React from "react";

const Counter = ({ onIcrement, onDelete, id, value }) => {
  function getBadgeClasses() {
    let classes = "badge m-2 badge-";
    classes += value === 0 ? "warning" : "primary";
    return classes;
  }

  function formatCount() {
    return value === 0 ? "Zero" : value;
  }

  return (
    <div>
      <span className={getBadgeClasses()}>{formatCount()}</span>
      <button
        onClick={() => onIcrement(id)}
        className="btn btn-secondary btn-sm"
      >
        Increment
      </button>
      <button
        onClick={() => onDelete(id)}
        className="btn btn-danger btn-sm m-2"
      >
        Delete
      </button>
    </div>
  );
};

export default Counter;

and your Counters.jsx like this:

import React, { useState} from 'react';
import Counter from './counter.jsx'

const init = {
  counters: [
            { id: 1, value: 4},
            { id: 2, value: 0},
            { id: 3, value: 0},
            { id: 4, value: 0}
        ]
}

const Counters = () => {
  const [state, setState] = useState(init);

  const handleIncrement = (counterId) => {
    const counters = [...state.counters];
    const index = counters.findIndex((counter) => counter.id === counterId);
    counters[index] = { ...counters[index] };
    counters[index].value++;
    setState({ counters });
  }

  const handleReset = () => {
  }

  const handleDelete = (counterId) => {
      const counterDup = [...state.counters.filter(c => c.id !== counterId)];
      setState({ counters: counterDup });
  }

    return (
        <div>
            <button onClick={handleReset} className="btn btn-primary btn-sm m-2">Reset</button>
            {state.counters.map(counter => <Counter key={counter.id} onIcrement={handleIncrement} onDelete={handleDelete} id={counter.id}  value={counter.value}/>)}
        </div>
    );
}

export default Counters;

and everything should still work fine.

you may as well check out the codesandbox reference below

Codesandbox

Goodluck!

Your code is halfway there, so props to you.

The problem you have here is in your handleIncrement function and the arguments you're passing to it from your Counter component.

When a user clicks in the increment button, this is what you pass:

<button
  onClick={() => this.props.onIcrement(this.props)}
  className="btn btn-secondary btn-sm">
Increment
</button>

In this piece right here you're passing the ENTIRE props object:

this.props.onIcrement(this.props)

This means your handleIncrement function will get as an argument an object like:

{onIcrement: ƒ (), onDelete: ƒ (), id: 1, value: 4} .

By doing that, inside handleIncrement , you're actually using an object to find the index, which is not what you want ( counter below will be an object):

const index = counters.indexOf(counter);

I suggest you pass something you can easily use to find the counter index, such as the counter ID like you're already doing with your onDelete .

That piece of code inside the Counter component would look like:

<button
  onClick={() => this.props.onIcrement(this.props.id)}
  className="btn btn-secondary btn-sm"
>
  Increment
</button>

In your handleIncrement function, you just need to find the index for that counter ID. For that, you can use the JavaScript array function findIndex :

const index = counters.findIndex((counter) => counter.id === counterId);

In the end, your handleIncrement function would look like this:

  handleIncrement = (counterId) => {
    const counters = [...this.state.counters];
    const index = counters.findIndex((counter) => counter.id === counterId);
    counters[index] = { ...counters[index] };
    counters[index].value++;
    this.setState({ counters });
  };

编辑紫色萤火虫5k572

You have a few errors in your code, I did a little structuring on your component, but I strongly suggest you look for some getting started tutorials.

  1. You shouldn't mutate state variables other than calling setState

  2. You should use constructor , componentDidUpdate , componentDidMount as needed.

  3. Read the documentation to get a better feeling

    import React, { Component} from 'react'; import Counter from './counter.jsx' class Counters extends Component { constructor(props){ this.state = { counters: [ { id: 1, value: 4}, { id: 2, value: 0}, { id: 3, value: 0}, { id: 4, value: 0} ] }; } const handleIncrement = (counter) => { const counters = [...this.state.counters]; const index = counters.indexOf(counter); counters[index] = {...counters[index]}; counters[index].value++; this.setState({ counters }); } const handleReset = () => { } const handleDelete = (counterId) => { const countersCopy = [...this.state.counters] countersCopy.filter(c => c.id;== counterId). this:setState({ counters; countersCopy }). } render() { return ( <div> <button onClick={this.handleReset} className="btn btn-primary btn-sm m-2">Reset</button> {this.state.counters.map(counter => <Counter key={counter.id} onIcrement={this.handleIncrement} onDelete={this.handleDelete} id={counter.id} value={counter;value}/>)} </div> ); } } export default Counters;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM