I have a Layout
ponent that contains a state called isLightMode
, which I would like to pass as a prop for other ponents to access and use.
Here is my Layout
ponent:
import React from 'react'
import Main from '../ponents/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>
)
}
}
In particular, I have a Home
ponent that I would like to access the isLightMode
prop:
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
And here is my Main
ponent where I can access props.isLightMode
, but I want to access it from Home
ponent:
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
So how can I access the isLightMode
prop that is in the Layout
ponent from my Home
ponent?
I have a Layout
ponent that contains a state called isLightMode
, which I would like to pass as a prop for other ponents to access and use.
Here is my Layout
ponent:
import React from 'react'
import Main from '../ponents/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>
)
}
}
In particular, I have a Home
ponent that I would like to access the isLightMode
prop:
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
And here is my Main
ponent where I can access props.isLightMode
, but I want to access it from Home
ponent:
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
So how can I access the isLightMode
prop that is in the Layout
ponent from my Home
ponent?
- 5 You have to either pass the prop down through ponents (known as prop drilling), or use the context API reactjs/docs/context.html. – Tom Commented Jul 24, 2019 at 21:15
-
@Tom - So before context API, there would be no way for me to access
isLightMode
fromHome
ponent since I am not passing the prop directly fromLayout
toHome
? Just trying to understand if there are also other possible ways (even if they are less efficient) than Context API – kimbaudi Commented Jul 24, 2019 at 22:14 - You could also use Redux but again, that uses the context API. The only other way is prop drilling. – Tom Commented Jul 24, 2019 at 23:33
-
you can make use of React hook's
useContext
– Joseph D. Commented Jul 25, 2019 at 1:14
4 Answers
Reset to default 3Context
Use createContext
to wrap all ponents that you need to access the state.
And consume it via Consumer
(much cleaner with hooks).
const Theme = React.createContext({ isLightMode: true });
// Consume the context in Home and in Layout
const App = () => (
<Theme.Provider>
<Home />
</Theme.Provider>
);
Pass reference
This is an example with hooks, can be achieved with classes.
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>
);
};
Pass properties
Home
--- Layout
--- Navigation
--- Main
How your code is structured isLightMode
needs to be in Home
so it will be in scope for Layout
too:
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>
);
};
State Manager
When context bees too overwhelming you should consider using state managing libraries like Redux and MobX.
The better way is to make context and consumer from there. But you can use React.cloneElement for passing data :
So in your case, you can do something like this :
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>
);
}
}
And in your home ponent you can consume directly :
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"}</>;
};
Here is working link : https://codesandbox.io/s/busy-resonance-x69gm
You can get it done by passing a callback function as a prop from the Home Component to the Layout Component, so as soon as the state of the isLightMode gets changed you can call that function and change the isLightMode value in the Home.
const Home = props => {
return (
<Layout
onChangeMode={(mode) => setLightMode(mode)}
>
<img src={props.isLightMode ? AvatarLight : AvatarDark} />
</Layout>
)
}
So the toggleTheme will bee,
toggleTheme() {
this.setState(state => ({
isLightMode: !state.isLightMode,
}), () => {
this.props.onChangeMode(this.state.isLightMode);
})
}
In the Home Component, you will have to use useState hook or state to maintain the value of the isLightMode and pass it to the img tag from it.
Hope so this is what was required by you, and it solves your issue.
Solution using Context API:
Steps:
- create
Context
object andContext.Provider
ponent - wrap your root element in the
Context.Provider
ponent - use
contextType
property (orContext.Consumer
) to consumecontext
1. create Context
object and Context.Provider
ponent:
import react from 'React'
const defaultValue = {
isLightMode: true,
toggleTheme: () => {},
}
// create Context object called ThemeContext
const ThemeContext = React.createContext(defaultState)
// create Context.Provider ponent 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 })
}
ponentDidMount() {
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. wrap your root element in the Context.Provider
ponent:
import React from 'react'
import { ThemeProvider } from './src/context/ThemeContext'
export const wrapRootElement = ({ element }) => (
<ThemeProvider>{element}</ThemeProvider>
)
3. use contextType
property to consume context
:
Home ponent
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>
)
}
}
Navigation ponent
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>
)
}
}
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744306784a4567752.html
评论列表(0条)