如何使用钩子“提升” react组件中的setState函数?

时间:2020-02-19 08:30:29

标签: reactjs react-hooks

我有一个共享状态的组件,需要同步。

一种状态设定了当前的音阶类型,它应该支持所有可能的音阶,但是我从小调和大调开始。第二状态根据所选的音阶类型为所选的音符名称设置正确的音符。我的工作大部分都在进行,但是我需要更好地同步状态,以便一个可以对另一个做出反应。

反应社区中的一些人已经建议“提升状态”,我正在努力弄清楚如何正确地进行操作,尤其是使用钩子。

因此,我已经能够弄清楚如何将组件上设置的音阶音符的状态提升为一些充满阵列的对象,但是现在我需要弄清楚如何提升{ {1}}在下面的代码中起作用,并且这些音符名称成分中有12个,并且每个都将不同的音阶设置为onClick状态:

notes

因此,现在我需要弄清楚如何将onClick道具中的箭头功能“提升”到它自己的功能中,但是我不知道该怎么做,因为要求我需要传递不同的内容状态设置为该函数的每个实例,但要使该状态与另一状态保持同步,该状态是设置注释按钮随后对其进行反应的音阶类型。

更新:

经仔细检查,我相信状态如下:

<NoteInput
  id="c"
  scale={scale}
  type="radio"
  name="notes"
  label="c"
  value="c"
  onClick={
    () => setNotes(
      scale === 'minor' ? scalePatterns['c-minor'] :
      scale === 'major' ? scalePatterns['c-major'] :
      ''
    )
  }
/>
<NoteLabel
  whileHover={{ scale: 1 }}
  whileTap={{ scale: 0.9 }}
  scale={scale}
  htmlFor="c"
>
  {
    scale === 'major' ? 'C' :
    scale === 'minor' ? 'C' :
    'C'
  }
</NoteLabel>

...音阶类型将影响选择哪个根音符(例如C#或Db)以及音阶本身的音符。然后,音阶音符还需要结合音阶类型知道根音。

1 个答案:

答案 0 :(得分:0)

根据我对您的问题的了解,您的状态具有以下依赖性-

Root Note       Scale
        \       /
         \     /
          Notes

notes状态变量仅在root note name (c/d/e etc)scale (major/minor etc)中有更改时才更改。您可以将所有与比例尺相关的状态抽象为一个上下文,并在各个组件中使用它,如下所示。您可以使用useEffect钩子的第二个参数来监听状态变量的变化并执行side-effects

ScalesContext.js

...

const SCALE_PATTERNS = {
  "c-major": ["c", "d", "e", "f", "g", "a", "b"],
  "c-minor": ["c", "d", "eflat", "f", "g", "aflat", "bflat"]
};

const ScalesContext = createContext(null);

export const ScalesContextProvider = ({ children }) => {
  const [rootNote, setRootNote] = useState(""); // 'c'
  const [scale, setScale] = useState(""); // 'major'
  const [notes, setNotes] = useState([]); // ['c', 'd', ...]

  // On scale/rootNote state change -> update notes
  useEffect(() => {
    const key = `${rootNote}-${scale}`;
    setNotes(SCALE_PATTERNS[key]);
  }, [scale, rootNote]);

  return (
    <ScalesContext.Provider
      value={{ rootNote, scale, notes, setScale, setRootNote }}
    >
      {children}
    </ScalesContext.Provider>
  );
};

export const useScalesContext = () => useContext(ScalesContext);

App.js

...

export default function App() {
  return (
    <div className="App">
      // Hook up Provider around ScaleSelect component
      <ScalesContextProvider>
        <ScaleSelect />
      </ScalesContextProvider>
    </div>
  );
}

ScaleSelect.js

...

export const ScaleSelect = () => {
  const state = useScalesContext();
  const { rootNote, scale, notes, setScale, setRootNote } = state;

  return (
    <div>
      <p>Root Note: {rootNote}</p>
      <p>Scale: {scale}</p>
      <input
        type="text"
        value={rootNote}
        placeholder="Root Note (try 'c')"
        onChange={e => setRootNote(e.target.value)}
      />
      <input
        type="text"
        placeholder="Scale (try 'major')"
        value={scale}
        onChange={e => setScale(e.target.value)}
      />
      <p>Notes: {JSON.stringify(notes)}</p>
    </div>
  );
};

我在这里有一个有效的示例-https://codesandbox.io/s/scales-notes-d8o18?fontsize=14&hidenavigation=1&theme=dark