ReactJS Lifting State Up Part 4

Hıdır Volkan Sönmez
6 min readJan 22, 2024

We will ensure data transfer by communicating between child components and parent components.

As an example, I will use a small part of a mobile game that I am trying to make for my hobby by simplifying it.

Let’s talk about what we will do, we have a player. Our player has various abilities: such as fishing, lumbering; For us, these two abilities will be sufficient. As he uses his abilities, his ability increases by one point.

User Data

const user = {
money: 100,
skills: {
fishing: {
name: "Fishing",
defname: "fishing",
product: "rawFish",
point: 53,
tool: "fishingPole"
},
lumberjacking: {
name: "Lumberjacking",
defname: "lumberjacking",
product: "wood",
point: 27,
tool: "axe"
}
},
bag: {
fishingPole: {
defname: "fishingPole",
quantity: 1,
price: 8
},
axe: {
defname: "axe",
quantity: 1,
price: 8
}
}
};

After adding the player’s data, we can start creating our components. I will proceed in small steps and try to explain everything I did. We continue to work on “index.js”.

class Game extends React.Component{
render(){
return(
<div className="game">
</div>
);
}
}

ReactDOM.render(<Game/>, document.getElementById("root"));

We created our game component and rendered a <div>. Now let’s add the “html” sections where the other objects of the game will be located. Then, we will divide the “html” sections into parts and turn them into components.

class Game extends React.Component{
render(){
return(
<div className="game">
<div className="header">
<span className="user_money">Money: </span>
</div>
<ul className="user_skills"></ul>
<ul className="user_bag"></ul>
</div>
);
}
}

We also prepared “html” sections where our player’s money, skills and bag will appear. Now let’s send the player data to our <Game> component and print the player’s money on the screen.

class Game extends React.Component{
render(){
return(
<div className="game">
<div className="header">
<span className="user_money">Money: {this.props.user.money}</span>
</div>
<ul className="user_skills"></ul>
<ul className="user_bag"></ul>
</div>
);
}
}

ReactDOM.render(<Game user={user}/>, document.getElementById("root"));

I added a property named “user” to the <Game> component and used “props” in the component to print the player’s money from the incoming data into the header section. We can consider the Header section as a separate component. I don’t need a “state” of its own for now. That’s why I make Functional Component.

const Header = (props) => {
return(
<div className="header">
<span className="user_money">Money: {props.userMoney}</span>
</div>
);
}


class Game extends React.Component{
render(){
return(
<div className="game">
<Header userMoney={this.props.user.money}/>
<ul className="user_skills"></ul>
<ul className="user_bag"></ul>
</div>
);
}
}

I created a function called <Header> and it took the “props” parameter. I sent the data about the player’s money to the <Header> component in the <Game> component with the “userMoney” property, and I reached this value with “props” in the <Header> component.

Now let’s create components for the player’s skills and bag. I will write these components as “function”, we can convert them to “class” if necessary.

const SkillList = (props) => {
console.log(props);
return(
<ul className="user_skills"></ul>
);
};
const Bag = (props) => {
console.log(props);
return(
<ul className="user_bag"></ul>
);
};

class Game extends React.Component{
render(){
return(
<div className="game">
<Header userMoney={this.props.user.money}/>
<SkillList skills={this.props.user.skills}/>
<Bag bag={this.props.user.bag}/>
</div>
);
}
}

Let’s list the player’s skills in the <SkillList> component.

const SkillList = (props) => {
console.log(props);console.log(Object.entries(props.skills))
const skillList = Object.entries(props.skills).map((skill, index) => {
return(
<li className="skill" key={index}>
<span>{skill[1].name} : </span>
<span>{skill[1].point}</span>
<div>Product: {skill[1].product}</div>
<button>Start Product</button>
</li>
);
});
return(
<ul className="user_skills">{skillList}</ul>
);
};

“props.skills” included in the component is an Object Literal. I converted this object into an array with the Object.entries() method to navigate through it with Map(). The second object of the “skill” parameter that comes in Map() works for us, so I used it as skill[1].

When the Start Product button is clicked, the talent will run and make a production. Additionally, the relevant skill score will increase by 1. For this reason, it seems much more logical to make each skill a separate component (class Skill). We can manage all skills-related codes within the component named <Skill>.

class Skill extends React.Component{
render(){
return(
<li className="skill">
<span>{this.props.skill.name} : </span>
<span>{this.props.skill.point}</span>
<div>Product: {this.props.skill.product}</div>
<button>Start Product</button>
</li>
);
}
}

const SkillList = (props) => {
console.log(props);console.log(Object.entries(props.skills))
const skillList = Object.entries(props.skills).map((skill, index) => {
return <Skill skill={skill[1]} key={index}/>;
});
return(
<ul className="user_skills">{skillList}</ul>
);
};

I created the component named <Skill> and used it in my list named <SkillList>. I sent skill[1] as property. Of course, I didn’t forget to add “key” to the <Skill> component in map()!

Let’s take the click event.

class Skill extends React.Component{
clickHandle(){
console.log(this);
}
render(){
return(
<li className="skill">
<span>{this.props.skill.name} : </span>
<span>{this.props.skill.point}</span>
<div>Product: {this.props.skill.product}</div>
<button onClick={this.clickHandle}>Start Product</button>
</li>
);
}
}

I created a method called “clickHandle” within the component. I added this method to the “onClick” section in <Button>. On <Button> click, console.log(this) is available. When we look at the console section of the browser, we will see that it returns “undefined”. For this, we need to bind the <Skill> component to the method. We will do this in the constructor section.

class Skill extends React.Component{
constructor(){
super();
this.clickHandle = this.clickHandle.bind(this);
}
clickHandle(){
console.log(this);
}
render(){
return(
<li className="skill">
<span>{this.props.skill.name} : </span>
<span>{this.props.skill.point}</span>
<div>Product: {this.props.skill.product}</div>
<button onClick={this.clickHandle}>Start Product</button>
</li>
);
}
}

Now we can increase the skill score by 1 and also display the object to be crafted.

clickHandle(){
console.log(this.props.skill.point);
console.log(this.props.skill.product);
}

When we edit the clickHandle() method as above, we can see the output in the console.

So how do we update the data?

There is currently no “state” in any components. Let’s keep the player’s data in the <Game> component. Let’s create a structure where we will send news to the <Game> component from the <Skill> component. Let the <Skill> component say, “The information regarding a skill of the player has changed, update it”…

If we create a “state” and keep the information about the skills in the <Skill> component, the <Game> component will not be aware of this “state”.

So, I will define a method in the <Game> component and send this method to the <Skill> component. The <Skill> component will also update the player’s relevant skill using this method: LIFTING STATE UP.

class Game extends React.Component{
constructor(){
super();
this.handleSkillPoint = this.handleSkillPoint.bind(this);
}
handleSkillPoint(skillName, skillPointValue){
console.log(skillPointValue);
}
render(){
return(
<div className="game">
<Header userMoney={this.props.user.money}/>
<Skills skills={this.props.user.skills} handleSkillPoint={this.handleSkillPoint}/>
<Bag bag={this.props.user.bag}/>
</div>
);
}
}

I first added a constructor to the <Game> component, then added the method named “handleSkillPoint”. This method takes the skill score and skill name as parameters. Then, I did the bind operation of the “handleSkillPoint” method in the constructor. Finally, I sent this method as a property to the <SkillList> component from the <Game> component.

The “handleSkillPoint” method is now included in the <SkillList> component. From here I will send it to the <Skill> component.

const SkillList = (props) => {
console.log(props);console.log(Object.entries(props.skills))
const skillList = Object.entries(props.skills).map((skill, index) => {
return <Skill skill={skill[1]} key={index} handleSkillPoint={props.handleSkillPoint}/>;
});
return(
<ul className="user_skills">{skillList}</ul>
);
};
“handleSkillPoint={props.handleSkillPoint}” 

Let’s talk about using this handleSkillPoint in the <Skill> component. For this, we had already written a method called “clickHandle” and displayed the skill score and production object with console.log(). Let’s send the skill score and skill name as parameters to the handleSkillPoint method.

clickHandle(){
const skillPoint = this.props.skill.point + 1;
const skillName = this.props.skill.defname;
this.props.handleSkillPoint(skillName, skillPoint);
}

I edited the “clickHandle” method in the <Skill> component as above. When we click on the “Start Product” button, the “handleSkillPoint” method in the <Game> component will show us the skill score received as a parameter on the console. Now let’s update the player data. We continue with the <Game> component.

class Game extends React.Component{
constructor(props){
super(props);
this.handleSkillPoint = this.handleSkillPoint.bind(this);
this.state = {
skills: props.user.skills,
bag: props.user.bag
}
}
handleSkillPoint(skillName, skillPointValue){
let userSkills = this.state.skills;
console.log(userSkills[skillName]);
}
render(){
console.log(this.state);
return(
<div className="game">
<Header userMoney={this.props.user.money}/>
<SkillList skills={this.props.user.skills} handleSkillPoint={this.handleSkillPoint}/>
<Bag bag={this.props.user.bag}/>
</div>
);
}
}

First of all, we added “state” into the constructor. We kept player information by breaking it down into state (skills, bag). In the “handleSkillPoint” method, we assigned the player’s skills to an object (userSkills) and accessed the player’s relevant skill by using the skill name as a parameter in the method.

handleSkillPoint(skillName, skillPointValue){
let userSkills = this.state.skills;
userSkills[skillName].point = skillPointValue;
this.setState({
skills: userSkills
});
console.log(this.state.skills[skillName]);
}

I made changes in the “handleSkillPoint” module as above. I set the skill score (skillPontValue) that comes as a parameter equal to the score of the relevant skill in the “userSkills” object.

userSkills[skillName].point = skillPointValue;

I sent player abilities to the state of the <Game> component with “setState”.

Alternative method:

We could create another “state” within the <Skill> component. In this “state”, we would keep the information about the skill and update the “state” of the <Skill> component on the “Start Product” button click. Then, we would send the “state” information of the <Skill> component to the <Game> component with the “clickHandle” method. I preferred to do it with the properties of the <Skill> component, without keeping the “state”.

Create the bag yourself and add the object written in the “product” section of that skill to the bag every time the skill is used.

We will do the next work on this <Game> example.

--

--