Reputation: 1009
I am trying to write a class component that uses values from this.context
to derive a state object. I have a method getStyle
that returns such an object. At constructor
time, I am calling this.getStyle
to start off with an already up-to-date state, and speed up the initial rendering.
However, it seems that, at constructor
call time, this.context
is undefined
, and it remains such until render
call time.
Am I doing something wrong in my code?
I don't recall any details related to such issue in React's (new) context API docs. this.context
is available at componentDidMount
time, however, this would require a setState
, which will cause an extra component re-render which I would like to avoid.
Below is the code that I'm trying to use:
import React from "react";
import { View, Text } from "react-native";
const defaultTheme = {
light: { backgroundColor: "white" },
dark: { backgroundColor: "black" }
};
const customTheme = {
light: { backgroundColor: "#EEE" },
dark: { backgroundColor: "#111" },
};
const MyContext = React.createContext(defaultTheme);
class Container extends React.PureComponent {
static contextType = MyContext;
constructor(props) {
super(props);
this.state = this.getStyle(); // TypeError: undefined is not an object (evaluating '_this.context[_this.props.colorScheme || "light"]')
// this.state = {}; // use this to try the componentDidMount alternative
}
componentDidMount = () => {
const style = this.getStyle();
this.setState(style);
}
getStyle = () => {
// this.props.colorScheme = "light" | "dark" | null | undefined
return this.context[this.props.colorScheme || "light"];
}
componentDidUpdate = (prevProps, prevState, snapshot) => {
if (prevProps.colorScheme !== this.props.colorScheme) {
const style = this.getStyle();
this.setState(style);
}
}
render = () => {
return <View style={this.state}>
{this.props.children}
</View>;
}
}
export default function App() {
return <MyContext.Provider value={customTheme}>
<Container>
<Text>Hello, world!</Text>
</Container>
</MyContext.Provider>
}
Upvotes: 2
Views: 1416
Reputation: 1009
There is another solution other than the ones posted above: context wrapper component, passing context as props.
You create a Wrapper component that in its render function uses the context value, and passes it on to the component where you need those props at constructor time:
class TheActualComponent extends React.PureComponent {
constructor(props) {
super(props);
console.log(props.context); // Now you've got'em
}
// ...
}
// SFC wrapper
function WrapperComponent(props) {
const contextProps = React.useContext(MyContext);
return <TheActualComponent {...props} context={contextProps}>
{props.children}
</TheActualComponent>;
}
// class component wrapper
class WrapperClassComponent extends React.PureComponent {
static contextType = MyContext;
render() {
return <TheActualComponent {...this.props} context={this.context}>
{this.props.children}
</TheActualComponent>;
}
}
Upvotes: 0
Reputation: 11
For anyone stumbling across this using React 18+, the only way I could get this to work was to access this.conext
inside of the render()
method in a class component.
Upvotes: 0
Reputation: 21
Try passing context to constructor:
constructor(props, context) {
// your code here
}
This solution might be deprecated according to this but you can try.
Upvotes: 2