【问题标题】:React App only re-renders on first state change and changes are not visible for second change even though the change is successfulReact App 仅在第一次状态更改时重新渲染,即使更改成功,第二次更改也不可见
【发布时间】:2021-02-04 21:00:03
【问题描述】:

我正在尝试在 React 应用中实现暗模式。我已经在我的子组件上进行了切换,以将主题更改为暗模式。因此,假设我们从暗模式开始并单击切换,然后主题更改为亮模式。现在,如果再次切换,主题不会改变,但状态值会改变。

据我了解,如果状态发生变化,组件应该重新渲染,但只会重新渲染一次!

请告诉我出了什么问题。

App.js
------
import themes from '../theme';
import Container from '../common/container';

const App = function(props) {

 const [darkMode, setDarkMode] = React.useState(true);

 const changeTheme = function(){
  console.log('Dark Mode is ' + darkMode + ', Setting it to ' + !darkMode)
  setDarkMode(!darkMode)
 }
 
 return (
  <Router>
    <ThemeProvider theme={ darkMode ? themes.darkTheme : themes.lightTheme}>
      <CssBaseline />
      <Container darkMode={darkMode} changeTheme={changeTheme}>
        </Container>
    </ThemeProvider>
  </Router>
 );
}

如您所见,App 组件的状态为 darkMode,并且其切换被传递给子组件 Container。

Container.js
------------

const Container = function(props) {
 return (
  <Box>
    <AppBar position="static">
        <Toolbar>
            .
            .
          <IconButton color="primary" onClick={(event) => props.changeTheme()}>{ props.darkMode ?
          <Brightness4Icon /> : <BrightnessHighIcon /> }</IconButton>

           <Button variant="contained" color="primary">Login</Button>
        </Toolbar>
    </AppBar>
    <Box>
      {props.children}
    </Box>
  </Box>
 )
}

所以在Container组件中,如果点击IconButton,就会调用App Component的changeTheme函数。

现在,这段代码的结果是 - 我只能在第一次单击时更改主题,无论是先暗模式还是亮模式。第二次及以后的点击不会改变主题。

另外,正如您所注意到的,我在 changeTheme 中有一个日志语句。 PF 随附的控制台屏幕截图。

因此,根据我的理解,如果 darkMode 发生变化,应用程序应该重新渲染。但只是第一次这样做。

感谢您的帮助!

【问题讨论】:

  • 我不确定,但您可以尝试在这里使用箭头功能吗? ' const changeTheme = function(){'
  • 我也在这里找到medium.com/heuristics/… 他们在更改主题时重新创建了 Material Theme
  • 每次都可以创建 MuiTheme!它与使用回调来更新状态的内部对象/键的概念相同,因为对象保持不变。

标签: reactjs


【解决方案1】:

我不确定您的代码有什么问题,但如果您使用的是Context,则无需将darkMode 传递给Container

我根据您的代码制作了一个快速工作示例,根据您的需要对其进行修改。

import React from 'react'
import ReactDOM from 'react-dom'

const ThemeContext = React.createContext("light");

function App() {
  const [darkMode, setDarkMode] = React.useState(true);

  const changeTheme = function(){
    console.log('Dark Mode is ' + darkMode + ', Setting it to ' + !darkMode)
    setDarkMode(!darkMode)
 }  

  return (
    <ThemeContext.Provider value={darkMode ? "dark" : "light"}>
      <Container changeTheme={changeTheme}/>
    </ThemeContext.Provider>
  )
}

function Container({changeTheme}) {
  const theme = React.useContext(ThemeContext);

  return <React.Fragment>
    {theme}

    <button onClick={changeTheme}>Toggle Theme</button>
  </React.Fragment>;
}

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

如您所见,在Container 中,您可以使用useContext 来获取主题值,无需将其作为属性传递,这就是上下文的全部意义所在。

你可以在这个CodeSandbox中测试上面的代码

如果不清楚,您可以在React Docs 中阅读有关useContext 挂钩的更多信息。还有一个主题上下文实现的例子。

【讨论】:

  • 我将 darkMode 传递给 Container 只是为了呈现正确的图标。即使在我的代码中,状态也会像您的示例一样发生变化,但不会发生重新渲染。感谢您指出上下文的用法。
  • 我会尝试使用上下文来实现,因为我认为这样会更干净。到目前为止,每次都可以创建 MuiTheme 对象。
猜你喜欢
  • 1970-01-01
  • 2018-08-13
  • 1970-01-01
  • 2021-04-21
  • 1970-01-01
  • 2021-10-22
  • 2016-03-26
  • 2018-11-29
相关资源
最近更新 更多