如何在不实际更改状态的情况下重新渲染 React 组件

时间:2021-02-15 16:17:50

标签: javascript reactjs

在我的 React 应用程序中,我有一个名为 Value 的组件,它在 DOM 树的多个级别上有多个实例。它的值可以显示或隐藏,点击它可以显示或隐藏(如翻转卡片)。

我想制作 2 个按钮,“全部显示”和“全部隐藏”,这将使 Value 组件的所有这些实例显示或隐藏。我在一个组件(称为 Cases)中创建了这些按钮,该组件是 Value 组件的每个实例的父级。它有一个名为 mode 的状态,单击按钮将其设置为“showAll”或“hideAll”。我使用 React Context 为 Value 组件提供这种选择的模式。

我的问题:在我单击“全部隐藏”按钮,然后通过单击使某些 Value 实例可见后,我无法再次隐藏所有这些实例。我猜是因为 Value 组件不会重新渲染,因为即使调用了 setMode("hideAll") 函数,它实际上并没有改变状态的值。

有没有办法在调用 Value 函数后重新渲染 setMode 实例,即使没有进行实际更改? 我对 React 和 Web 开发比较陌生,我不确定这是否是正确的方法,所以我也很乐意得到一些关于什么是更好的解决方案的建议。

这是我的组件的代码:

const ModeContext = React.createContext()

export default function Cases() {
  const [mode, setMode] = useState("hideAll") 
  return (
    <>
        <div>
           <button onClick={() => setMode("showAll")}>Show all answers</button>
           <button onClick={() => setMode("hideAll")}>Hide all answers</button>
        </div>
        <ModeContext.Provider value={mode}>
            <div>
                {cases.map( item => <Case key={item.name} {...item}/> ) }
            </div>
        </ModeContext.Provider>   
    </>
  )
}

export default function Value(props) {
  const mode = useContext(ModeContext)
  const [hidden, setHidden] = useState(mode === "showAll" ? false : true)

  useEffect(() => {
        if (mode === "showAll") setHidden(false)
        else if (mode === "hideAll") setHidden(true)
    }, [mode])

  return (
    hidden 
        ? <span className="hiddenValue" onClick={() => setHidden(!hidden)}></span> 
        : <span className="value" onClick={() => setHidden(!hidden)}>{props.children}</span>
  )
}

3 个答案:

答案 0 :(得分:1)

首先需要创建上下文,然后才能将其用作提供者或用户。

因此请确保将此添加到文件的顶部。

const ModeContext = React.createContext('hideAll')

就目前而言,由于未创建 ModeContext,因此 mode 组件中的 Value 应该未定义且永远不会更改。

如果您的组件位于单独的文件中,请确保同时导出 ModeContext 并将其导入到其他组件中。

示例

这是组织一切并保持简单的一种方法。

// cases.js

const ModeContext = React.createContext('hideAll')

export default function Cases() {
  const [mode, setMode] = useState("hideAll") 
  return (
    <>
        <div>
           <button onClick={() => setMode("showAll")}>Show all answers</button>
           <button onClick={() => setMode("hideAll")}>Hide all answers</button>
        </div>
        <ModeContext.Provider value={mode}>
            <div>
                {cases.map( item => <Case key={item.name} {...item}/> ) }
            </div>
        </ModeContext.Provider>   
    </>
  )
}

export function useModeContext() {
  return useContext(ModeContext)
}
// value.js

import { useModeContext } from './cases.js'

export default function Value(props) {
  const mode = useContext(ModeContext)
  const [hidden, setHidden] = useState(mode === "showAll" ? false : true)

  useEffect(() => {
        if (mode === "showAll") setHidden(false)
        else if (mode === "hideAll") setHidden(true)
    }, [mode])

  return (
    hidden 
        ? <span className="hiddenValue" onClick={() => setHidden(!hidden)}></span> 
        : <span className="value" onClick={() => setHidden(!hidden)}>{props.children}</span>
  )
}

附言我也犯过很多次这样的错误。

答案 1 :(得分:0)

您不应在 Value 组件中使用新状态。你的组件应该有一个 [only single of truth][1],在你的例子中是 mode。在您的上下文中,您还应该提供一个隐藏组件的功能,您可以调用 setHidden

更改 Value 组件,如下所示:

export default function Value(props) {
  const { mode, setHidden } = useContext(ModeContext)
  
  if(mode === "showAll") {
        return <span className="hiddenValue" onClick={() => setHidden("hideAll")}></span> 
        } else if(mode === "hideAll") { 
        return <span className="value" onClick={() => setHidden("showAll")}>{props.children}</span>
} else {
 return null; 
}
  )
} 

附言因为 mode 似乎是一个布尔值,所以您可以在 true 和 false 之间切换。 [1]:https://reactjs.org/docs/lifting-state-up.html

答案 2 :(得分:0)

有几种方法可以处理这种情况。

  1. 在父组件中移动状态。像这样跟踪父组件中的所有可见状态:

  const [visible, setVisibilty] = useState(cases.map(() => true))
...
<button onClick={() => setVisibilty(casses.map(() => false)}>Hide all answers</button>
...
 {cases.map((item, index) => <Case key={item.name} visible={visible[index]} {...item}/> ) }
  1. 在重置所有状态后重置模式:
const [mode, setMode] = useState("hideAll") 
useEffect(() => {
   setMode("")
}, [mode])