React hooks - 当状态更改为相同值时如何强制 useEffect 为 运行?

React hooks - how to force useEffect to run when state changes to the same value?

所以我正在构建一个鼓垫类型的应用程序,几乎所有的东西都在工作,除了这个。

编辑:全部放在codesandbox上,如果有人想看:codesandbox.io/s/sleepy-darwin-jc9b5?file=/src/App.js

const [index, setIndex] = useState(0);
const [text, setText] = useState("");
const [theSound] = useSound(drumBank[index].url)   

function playThis(num) {
  setIndex(num)
}

useEffect(()=>{
  if (index !== null) {
    setText(drumBank[index].id);
    theSound(index);
    console.log(index)
  }
}, [index])

当我按下一个按钮时,索引会更改为与该按钮关联的值,然后 useEffect 挂钩会播放该 索引 处数组中的声音。但是,当我多次按下同一个按钮时,它只播放一次,因为当 index 更改为相同的值时 useState 不会重新呈现应用程序。

我希望能够多次按下同一个按钮并让应用程序重新呈现,因此每次按下按钮时都使用 Effect 运行。谁能帮我怎么做?

您是否考虑过将那些离散的状态变量(值类型)组合成一个单一的引用类型状态对象?

您只需同时设置整个状态,而不是在索引更改时产生设置文本的效果。

只要您确保这是一个新的 object/reference 效果就会触发。然后effect只负责根据当前状态播放声音。

这是一些示例代码

const [state, setState] = useState({ index: 0, text: '', }); 
const playThis = (num) => { 
    setState({ index: num, text: drumBank[num].id, }); 
}; 
useEffect(() => { 
    theSound(state.index); 
}, [state]);

这是我从您的沙盒中得出的结论。

  1. 根据文档,每个 useSound 只是一个声音,因此当尝试将索引更新到音库以通过 React 状态使用时,播放的声音将始终至少是一个渲染周期延迟。我建议创建一个新的自定义钩子来封装您的 9 种鼓声。

    useDrumBank 使用 drumbank 数组并将 9 种鼓声实例化到一个数组中。

    const useDrumBank = (drumbank) => {
      const [drum0] = useSound(drumbank[0].url);
      const [drum1] = useSound(drumbank[1].url);
      const [drum2] = useSound(drumbank[2].url);
      const [drum3] = useSound(drumbank[3].url);
      const [drum4] = useSound(drumbank[4].url);
      const [drum5] = useSound(drumbank[5].url);
      const [drum6] = useSound(drumbank[6].url);
      const [drum7] = useSound(drumbank[7].url);
      const [drum8] = useSound(drumbank[8].url);
    
      return [drum0, drum1, drum2, drum3, drum4, drum5, drum6, drum7, drum8];
    };
    
  2. 更新组件逻辑以将 drumBank 数组传递给新的自定义挂钩。

    const sounds = useDrumBank(drumBank);
    

完整代码如下:

function App() {
  useEffect(() => {
    document.addEventListener("keypress", key);

    return () => document.removeEventListener("keypress", key);
  }, []);

  const [text, setText] = useState("");
  const sounds = useDrumBank(drumBank);

  function playThis(index) {
    drumBank[index]?.id && setText(drumBank[index].id);
    sounds[index]();
  }

  function key(e) {
    const index = drumBank.findIndex((drum) => drum.keyTrigger === e.key);
    index !== -1 && playThis(index);
  }

  return (
    <div id="drum-machine" className="drumpad-container">
      <div id="display" className="drumpad-display">
        <p>{text}</p>
      </div>
      <button className="drum-pad" id="drum-pad-1" onClick={() => playThis(0)}>
        Q
      </button>
      <button className="drum-pad" id="drum-pad-2" onClick={() => playThis(1)}>
        W
      </button>
      <button className="drum-pad" id="drum-pad-3" onClick={() => playThis(2)}>
        E
      </button>
      <button className="drum-pad" id="drum-pad-4" onClick={() => playThis(3)}>
        A
      </button>
      <button className="drum-pad" id="drum-pad-5" onClick={() => playThis(4)}>
        S
      </button>
      <button className="drum-pad" id="drum-pad-6" onClick={() => playThis(5)}>
        D
      </button>
      <button className="drum-pad" id="drum-pad-7" onClick={() => playThis(6)}>
        Z
      </button>
      <button className="drum-pad" id="drum-pad-8" onClick={() => playThis(7)}>
        X
      </button>
      <button className="drum-pad" id="drum-pad-9" onClick={() => playThis(8)}>
        C
      </button>
    </div>
  );
}

演示

使用说明

No sounds immediately after load

For the user's sake, browsers don't allow websites to produce sound until the user has interacted with them (eg. by clicking on something). No sound will be produced until the user clicks, taps, or triggers something.

让按键持续工作似乎是个问题,我没有立即想到解决方案,但至少在这一点上,按钮点击有效并且声音同步播放。