如何在无状态组件中有条件地更新状态?

时间:2019-05-04 19:41:20

标签: reactjs react-hooks

我有两个组成部分。 MyButton和MyLabel组件。我创建了3个MyButton组件和3个MyLabel组件。每个按钮都有一个不同的增量值。当您单击按钮时,应该更新相应的标签,而不是所有标签。目前所有标签都在更新。

function MyButton(props) {
  const onclick = () => {
    props.onNumberIncrement(props.toBeIncremented);
  };
  return <button onClick={onclick}>+{props.toBeIncremented}</button>;
}

const MyLabel = function(props) {
  return <label>&nbsp;&nbsp;&nbsp;&nbsp;{props.counter}</label>;
};

function App(props) {
  const [counter, mySetCounter] = React.useState(0);

  const handleClick = (incrementValue) => {
    mySetCounter(counter + incrementValue);
  };

  return (
    <div>
      <MyButton
        counter={counter}
        onNumberIncrement={handleClick}
        toBeIncremented={5}
      />
      <MyButton
        counter={counter}
        onNumberIncrement={handleClick}
        toBeIncremented={10}
      />
      <MyButton
        counter={counter}
        onNumberIncrement={handleClick}
        toBeIncremented={15}
      />
      <br />
      <MyLabel counter={counter} />
      <MyLabel counter={counter} />
      <MyLabel counter={counter} />
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));

jsfiddle: click here

2 个答案:

答案 0 :(得分:1)

创建带有按钮/标签对及其本地状态和步骤的生成器。生成按钮和标签,然后渲染它们:

const useGenerateButtonAndLabel = step => {
  const [counter, mySetCounter] = React.useState(0);
  
  const onclick = React.useCallback(
    () => mySetCounter(counter + step), 
    [step, counter]
  );

  return [
    <button onClick={onclick}>+{step}</button>,
    <label>&nbsp;&nbsp;&nbsp;&nbsp;{counter}</label>
  ];
};

function App(props) {
  const [button1, label1] = useGenerateButtonAndLabel(5);
  const [button2, label2] = useGenerateButtonAndLabel(10);
  const [button3, label3] = useGenerateButtonAndLabel(15);

  return (
    <div>
      {button1}
      {button2}
      {button3}
      <br />
      {label1}
      {label2}
      {label3}
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById('demo'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="demo"></div>

如果您还需要总计,则每个生成的对也可以返回其当前的counter,并且可以将它们加到父级中。在此示例中,我还使用Array.from(),映射和缩小来自动创建/渲染项目。

const useGenerateButtonAndLabel = step => {
  const [counter, mySetCounter] = React.useState(0);
  
  const onclick = React.useCallback(
    () => mySetCounter(counter + step), 
    [step, counter]
  );
  
  // step is used here is a key, but if step is not unique, it will fail. You might want to generate a UUID here
  return [
    <button key={step} onClick={onclick}>+{step}</button>,
    <label key={step}>&nbsp;&nbsp;&nbsp;&nbsp;{counter}</label>,
    counter
  ];
};

const sum = items => items.reduce((r, [,, counter]) => r + counter, 0);

function App(props) {
  const items = Array.from({ length: 5 }, 
    (_, i) => useGenerateButtonAndLabel(5 * (i + 1))
  );

  return (
    <div>
      {items.map(([button]) => button)}
      <br />
      {items.map(([, label]) => label)}
      
      <div>Total: {sum(items)}</div>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById('demo'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="demo"></div>

答案 1 :(得分:0)

这是另一个使用单个状态变量的解决方案:

function App(props) {
      const [appState, setAppState] = React.useState([
       {value: 0, incrementValue: 5},
       {value: 0, incrementValue: 10},
       {value: 0, incrementValue: 15}
      ]);

      const handleClick = clickedIndex => () => {
        setAppState(
          appState.map((item, index) => clickedIndex !== index ? 
            item : ({...item, value: item.value + item.incrementValue})
          ) 
        );
      };

      return (
        <div>
         {
           appState.map(({value, incrementValue}, index) => (
            <MyButton
              key={index}
              counter={value}
              onNumberIncrement={handleClick(index)}
              toBeIncremented={incrementValue}
            />
           ));
          }
          <br />
          {
            appState.map(
              ({value}, index) => <MyLabel key={index} counter={value} />
            );
          }
         </div>
      );
}