Passing State as Props

Viewed 109

EDITED

So I have a Counter button component which I am rendering in my App. My App contains the state for the counter but how do I pass it as props to the Counter component? At the moment Counter has its own state but I want it to be using the state in App.

Also I am trying to make a universal counter button in a separate component which will increment/decrement the duplicate Counter buttons. But I have the same issue where I am not sure how to correclty pass the App's state as props into this universal button.

Universal Counter:

class UniversalCounter extends Component {
  constructor(props) {
    super(props);
    const allCounter = [Counter]
    this.state = allCounter;
  }

  incrementCount() { 
    this.setState(prevState => ({ allCounter: prevState.allCounter + 1 }));
  }

  decrementCount() {
    this.setState(prevState => ({ allCounter: prevState.allCounter - 1 }));
  }

  render() {
    let { allCounter } = this.state;

    return (
      <div>
        <h2>All Counters</h2>
        <button onClick={() => this.incrementCount(allCounter)}>increase all</button>
        <button onClick={() => this.decrementCount(allCounter)}>decrease all</button>
      </div>
    );
  }

Counter Component:

class Counter extends Component {
  constructor(props) {
    super(props);
    this.state = { counter: 0 };
  }

  incrementCount() {
     this.setState(prevState => ({ counter: prevState.counter+ 1 }));
  }

  decrementCount() {
    this.setState(prevState => ({ counter: prevState.counter- 1 }));
  }

  render() {
    let { counter} = this.state;

    return (
      <div>
        <h2>Count: {counter}</h2>
        <button onClick={() => this.incrementCount(counter)}>+</button>
        <button onClick={() => this.decrementCount(counter)}>-</button>
      </div>
    );
  }
}

App:

class App extends Component {
  constructor(props) {
    super(props);
    this.state = { counter: 0 };
  }

  render() {
    let { counter } = this.state;

    return (
      <div className="App">
        <Counter/>
        <Counter/>
        <Counter/>
        <UniversalCounter />
      </div>
    );
  }
}

export default App;
1 Answers

Loft state and handlers to closest common ancestor. Counter then becomes a "dumb" component.

Lifting State Up

Counter.jsx

const Counter = ({ count, onIncrementClick, onDecrementClick }) => (
  <div>
    <h2>Count: {count}</h2>
    <button type="button" onClick={onIncrementClick}>
      +
    </button>
    <button type="button" onClick={onDecrementClick}>
      -
    </button>
  </div>
);

UniversalCounter.jsx

const UniversalCounter = () => {
  const [counters, setCounters] = useState([0, 0, 0]);

  const incrementCount = (index, c) => () =>
    setCounters(counts =>
      counts.map((count, i) => (index === i ? count + c : count))
    );

  const incrementCountAll = c => () =>
    setCounters(counts => counts.map((count, i) => count + c));

  return (
    <div>
      <h2>All Counters</h2>
      {counters.map((count, i) => (
        <Counter
          key={i}
          count={count}
          onIncrementClick={incrementCount(i, 1)}
          onDecrementClick={incrementCount(i, -1)}
        />
      ))}

      <button type="button" onClick={incrementCountAll(1)}>
        Increase All
      </button>
      <button type="button" onClick={incrementCountAll(-1)}>
        Decrease All
      </button>
    </div>
  );
};

Edit multiple counters common state

If (for some not DRY reason at all) wanted to render each Counter individually:

return (
  <div>
    <h2>All Counters</h2>

    <Counter
      count={counters[0]}
      onIncrementClick={incrementCount(0, 1)}
      onDecrementClick={incrementCount(0, -1)}
    />
    <Counter
      count={counters[1]}
      onIncrementClick={incrementCount(1, 1)}
      onDecrementClick={incrementCount(1, -1)}
    />
    <Counter
      count={counters[2]}
      onIncrementClick={incrementCount(2, 1)}
      onDecrementClick={incrementCount(2, -1)}
    />

    <button type="button" onClick={incrementCountAll(1)}>
      Increase All
    </button>
    <button type="button" onClick={incrementCountAll(-1)}>
      Decrease All
    </button>
  </div>
);
Related