Reputation: 7092
I'm trying to set up state for a portion of my web game that allows players to upload photos of their monsters.
I have a parent component called <MonsterContainer>
. It contains two child components: <MonsterPortraitUpload>
and <MonsterViewer>
I am setting state in <MonsterContainer>
<MonsterPortraitUpload>
Will allow users to upload photos and it will add to the MonsterPortraits array in the state.
<MonsterViewer>
Will show all the photos of the monster in the array.
How can I ensure that my two child components have access to this state?
Here is what I have now:
class MonsterContainer extends Component {
constructor() {
super();
this.state = {
MonsterPortraits: []
};
}
render() {
const { monsterId } = this.props;
return (
<div>
<div>
<MonsterPortraitUpload monsterId={monsterId}/>
</div>
<div>
<MonsterViewer monsterId={monsterId}/>
</div>
</div>
);
}
}
Right now I am trying to write the state to the console in <MonsterViewer>
but I get this error:
Uncaught TypeError: Cannot read property 'MonsterPortraits' of null
Upvotes: 1
Views: 373
Reputation: 160191
Child components don't have access to parent state; data flows one way: down.
If a child component needs access to parent state that state should be passed as a property.
If that is insufficient for your needs (e.g., a deeply-nested hierarchy where children need to update state at a much higher level) then you'll probably want a different state management system, e.g., Redux/etc.
In your case, as presented, passing down the array of portraits to <MonsterView />
would seem sufficient, and <MonsterPortraitUpload />
would take a function prop that adds to the container's state (e.g., add a portrait).
This is a fundamental concept in React and similar: dumb components, smart containers. Dump components take data and functions, containers manage state. This breaks down in deep hierarchies because you end up having to pass properties down multiple levels.
That may be address via React Contexts, or injecting properties into cloned children, etc., but whether or not those are good solutions depends on your architecture. In general, a different form of state management can make problems like this go away by introducing a thin layer of complexity.
To modify the container's state you pass a hander; very off-the-cuff:
class MonsterContainer extends Component {
// Etc.
addPortrait(someData) {
this.setState([ ...this.state.MonsterPortraits, someData ]);
}
render() {
// Etc.
<MonsterPortraitUpload addHandler={this.addPortrait} />
In <MonsterPortraitUpload />
you'd create the data however (e.g., a form) and then on a user action (e.g., a button press) you call the container's handler function passing whatever data:
class MonsterPortraitUpload extends Component {
addPortait() {
// Marshall the data however, then
this.props.addPortrait(newData);
}
// Etc.
}
Upvotes: 2
Reputation: 2608
An error that you show, definitely not from attached code. If you need to pass state down use this
class MonsterContainer extends Component {
constructor() {
super();
this.state = {
MonsterPortraits: []
};
}
render() {
return (
<div>
<div>
<MonsterPortraitUpload monsterPortraits={this.state.MonsterPortraits}/>
</div>
<div>
<MonsterViewer monsterPortraits={this.state.MonsterPortraits}/>
</div>
</div>
);
}
}
So in MonsterPortraitUpload
component you may use this.props.monsterPortraits
Upvotes: 1