簡體   English   中英

如果我的組件作為 ReactJS 中的兒童道具傳遞給布局組件,如何訪問布局組件中的道具?

[英]How to access a prop in a layout component if my component is being passed to the layout component as a chilren prop in ReactJS?

我有一個Layout組件,其中包含一個名為isLightMode的狀態,我想將其作為道具傳遞給其他組件以供訪問和使用。

這是我的Layout組件:

import React from 'react'
import Main from '../components/main'

export default class Layout extends React.Component {
  constructor(props) {
    super(props)
    this.state = { isLightMode: true }
    this.toggleTheme = this.toggleTheme.bind(this)
  }

  toggleTheme() {
    this.setState(state => ({
      isLightMode: !state.isLightMode,
    }))
  }

  render() {
    const { isLightMode } = this.state
    const { children } = this.props
    return (
      <div>
        <Navigation
          isLightMode={isLightMode}
          onToggleTheme={this.toggleTheme}
        />
        {children && <Main isLightMode={isLightMode}>{children}</Main>}
      </div>
    )
  }
}

特別是,我有一個Home組件,我想訪問isLightMode

import React from 'react'
import Layout from '../layouts/layout'
import AvatarLight from '../../content/assets/img/avatar-light.png'
import AvatarDark from '../../content/assets/img/avatar-dark.svg'

const Home = props => {
  return (
    <Layout>
      <img src={props.isLightMode ? AvatarLight : AvatarDark} />
    </Layout>
  )
}
export default Home

這是我的Main組件,我可以在其中訪問props.isLightMode ,但我想從Home組件訪問它:

import React from 'react'

const Main = (props) => {
  return (
    <main className="main">
      <div>lightmode:{props.isLightMode}</div>
      <div className="container">{props.children}</div>
    </main>
  )
}
export default Main

那么如何從我的Home組件訪問Layout組件中的isLightMode呢?

語境

使用createContext來包裝訪問狀態所需的所有組件。 通過Consumer使用它(使用鈎子更清潔)。

const Theme = React.createContext({ isLightMode: true });

// Consume the context in Home and in Layout
const App = () => (
  <Theme.Provider>
    <Home />
  </Theme.Provider>
);

通過參考

這是一個帶有鈎子的例子,可以用類來實現。

const Home = props => {
  const isLightRef = createRef(false);
  // In layout call props.isLightPref.current = true / false.
  return (
    <Layout isLighRef={isLightRef}>
      <img src={isLightRef.current ? AvatarLight : AvatarDark} />
    </Layout>
  );
};

通行證屬性

Home
  --- Layout
         --- Navigation
         --- Main

您的代碼的結構方式isLightMode需要在Home因此它也將在Layout范圍內:

export default class Home extends React.Component {
  constructor(props) {
    super(props);
    this.state = { isLightMode: true };
    this.toggleTheme = this.toggleTheme.bind(this);
  }

  toggleTheme() {
    this.setState(state => ({
      isLightMode: !state.isLightMode
    }));
  }

  render() {
    return (
      <Layout isLightMode={this.toggleTheme} toggleTheme={this.toggleTheme}>
        <img src={this.isLightMode ? AvatarLight : AvatarDark} />
      </Layout>
    );
  }
}

const Layout = ({ children, isLightMode }) => {
  return (
    <div>
      <Navigation isLightMode={isLightMode} onToggleTheme={this.toggleTheme} />
      {children && <Main isLightMode={isLightMode}>{children}</Main>}
    </div>
  );
};

狀態管理器

當上下文變得太多時,您應該考慮使用狀態管理庫,如ReduxMobX

更好的方法是從那里創建上下文和消費者。 但是你可以使用React.cloneElement來傳遞數據:

所以在你的情況下,你可以做這樣的事情:

class Layout extends React.Component {
  constructor(props) {
    super(props);
    this.state = { isLightMode: true };
    this.toggleTheme = this.toggleTheme.bind(this);
  }

  toggleTheme() {
    this.setState(state => ({
      isLightMode: !state.isLightMode
    }));
  }

  render() {
    const { isLightMode } = this.state;
    const { children } = this.props;

    return (
      <div>{children && <Main isLightMode={isLightMode}>{children}</Main>}</div>
    );
  }
}

在您的家庭組件中,您可以直接使用:

const Main = props => {
  return (
    <main className="main">
      <div>lightmode:{String(props.isLightMode)}</div>
      <div className="container">
        {React.cloneElement(props.children, props)} // pass props data to children
      </div>
    </main>
  );
};
const Home = props => {
  return <>{props.isLightMode ? "AvatarLight" : "AvatarDark"}</>;
};

這是工作鏈接: https : //codesandbox.io/s/busy-resonance-x69gm

您可以通過將回調函數作為道具從 Home 組件傳遞給 Layout 組件來完成它,因此一旦 isLightMode 的狀態發生更改,您就可以調用該函數並更改 Home 中的 isLightMode 值。

const Home = props => {
  return (
    <Layout
      onChangeMode={(mode) => setLightMode(mode)}
    >
      <img src={props.isLightMode ? AvatarLight : AvatarDark} />
    </Layout>
  )
}

所以 toggleTheme 將變成,

toggleTheme() {
  this.setState(state => ({
    isLightMode: !state.isLightMode,
  }), () => {
    this.props.onChangeMode(this.state.isLightMode);
  })
}

在 Home 組件中,您將不得不使用 useState hook 或 state 來維護 isLightMode 的值並將其傳遞給 img 標簽。

希望這是您所需要的,它可以解決您的問題。

使用上下文 API 的解決方案:

腳步:

  1. 創建Context對象和Context.Provider組件
  2. 將您的根元素包裝在Context.Provider組件中
  3. 使用contextType屬性(或Context.Consumer )來使用context

1. 創建Context對象和Context.Provider組件:

import react from 'React'

const defaultValue = {
  isLightMode: true,
  toggleTheme: () => {},
}

// create Context object called ThemeContext
const ThemeContext = React.createContext(defaultState)

// create Context.Provider component called ThemeProvider
class ThemeProvider extends React.Component {
  state = {
    isLightMode: true,
  }

  toggleTheme = e => {
    e.preventDefault()
    const isLightMode = !this.state.isLightMode
    localStorage.setItem('isLightMode', JSON.stringify(isLightMode))
    this.setState({ isLightMode })
  }

  componentDidMount() {
    const isLightMode = JSON.parse(localStorage.getItem('isLightMode'))
    if (isLightMode) {
      this.setState({ isLightMode })
    }
  }

  render() {
    const { children } = this.props
    const { isLightMode } = this.state

    return (
      <ThemeContext.Provider
        value={{
          isLightMode,
          toggleTheme: this.toggleTheme,
        }}
      >
        {children}
      </ThemeContext.Provider>
    )
  }

}

2. 將您的根元素包裝在Context.Provider組件中:

import React from 'react'
import { ThemeProvider } from './src/context/ThemeContext'
export const wrapRootElement = ({ element }) => (
  <ThemeProvider>{element}</ThemeProvider>
)

3. 使用contextType屬性來消費context

主頁組件

import React from 'react'
import Layout from '../layouts/layout'
import ThemeContext from '../context/ThemeContext'
import AvatarLight from '../../content/assets/img/avatar-light.png'
import AvatarDark from '../../content/assets/img/avatar-dark.svg'

export default class Home extends React.Component {
  static contextType = ThemeContext

  render() {
    const { isLightMode } = this.context
    return (
      <Layout>
        <img src={isLightMode ? AvatarLight : AvatarDark} />
      </Layout>
    )
  }
}

導航組件

import React from 'react'
import Layout from '../layouts/layout'
import ThemeContext from '../context/ThemeContext'
import AvatarLight from '../../content/assets/img/avatar-light.png'
import AvatarDark from '../../content/assets/img/avatar-dark.svg'

export default class Home extends React.Component {
  static contextType = ThemeContext

  render() {
    const { isLightMode, toggleTheme } = this.context
    return (
      <nav>
        <a onClick={toggleTheme}>{isLightMode ? 'Light mode' : 'Dark mode'}</a>
      </nav>
    )
  }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM