React. How to call method of specific subchild in component tree - reactjs

I'm starting to learn React and wonder how the following theoretical problem can be solved.
Suppose I have such components.
class Game extends React.Component {
constructor(props) {
super(props);
this.state = {
galaxyData:{}
};
}
handleGalaxyCommand(cmd) {
...
}
render() {
return (
<Galaxy galaxyData={this.state.galaxyData} />
);
}
}
class Galaxy extends React.Component {
render() {
return (this.props.galaxyData.sectors.map((sector) =>
<Sector sectorData={sector.sectorData} />
)
);
}
}
class Sector extends React.Component {
render() {
return (this.props.sectorData.ships.map((ship) =>
<Ship shipData={ship.shipData} />
)
);
}
}
class Ship extends React.Component {
constructor(props) {
super(props);
this.state = {
x: this.props.shipData.inialX,
y: this.props.shipData.inialY,
};
}
moveTo(x,y){
...
}
render() {
return <div x={this.state.x} y={this.state.y} id={this.props.shipData.id}/>
}
}
I wrote the code quickly for an example only, so I apologize for any syntax errors.
So the component tree looks something like this.
<Galaxy>
<Sector>
<Ship/>
...
<Ship/>
</Sector>
<Sector>
<Ship/>
...
<Ship/>
</Sector>
</Galaxy>
There may even be thousands of ships.
The ship has a "moveTo" method, which starts the Timer to change the x and y variables in the state, which causes the re-render, the move effect.
Let's assume that the Game component receives the command via the "handleGalaxyCommand" method to make the ship start moving.
How to call the "moveTo" method on a ship that interests me?

This is actually possible in react :) in a very simple way.
But this works only in class-based components (not functional or hooks).
Basically, you can call any child's methods from the parent if you access it's refs
Something like:
class Parent extends Component {
childRef = null;
componentDidMount() {
//via ref you can call it
this.childRef.myCustomMethod();
}
render() {
return <Child ref={ref => this.childRef = ref} />
}
}
class Child extends Component {
myCustomMethod() {
console.log("call me ");
}
render() {
return <div />;
}
}
Check this part of the docs for more details: https://reactjs.org/docs/refs-and-the-dom.html#adding-a-ref-to-a-class-component

Related

Setting state of a created but not mounted React component

I have a react component I'm creating as a local variable. I'd like to tweak its state before attaching it to the DOM. A super-simplified version of the code looks like this:
class Demo extends React.Component {
constructor(props) {
super(props);
this.state = {foo: 2};
}
render() {
return <p>{this.state.foo}</p>;
}
}
class App extends React.Component {
render() {
let elem = <Demo/>;
elem.setState({foo:4});
}
}
(The real code has a point, but I'm posting the simplified test case so you don't have to read long irrelevancies)
I'm getting the error
TypeError: elem.setState is not a function
What does this error mean? I'm checked that element is an instance of Demo.
Is there a way to set the state at this time?
ETA: I know what props is. I really want to modify the element after creating it.
You can't setstate like this. If you want to manipulate state in the child component you have to add props to do that.
import React, { Component } from "react";
class Demo extends Component {
state = {
foo: this.props.foo || 0
};
componentDidUpdate(prevState) {
console.log(prevState.foo, this.props.foo);
if (prevState.foo !== this.props.foo) {
this.setState({ foo: this.props.foo });
}
}
render() {
return <button {...this.props}>{this.state.foo}</button>;
}
}
export default class App extends Component {
state = {
count: 0
};
clickMe = () => {
this.setState({
count: this.state.count + 1
});
};
render() {
return <Demo onClick={() => this.clickMe()} foo={this.state.count} />;
}
}
Here is working example https://codesandbox.io/s/festive-rhodes-redk7

Reactjs: Parent function call triggered by a child

So I am building my first react project and stumbled upon following problem:
In my App.js (main application) I got a function and render my components:
class App extends Component {
constructor(props) {
super(props);
this.candidateCounter = 0;
this.setCandidateVote = this.setCandidateVote.bind(this);
}
...
setCounter (name) {
this.candidateCounter++;
console.log(this.candidateCounter);
}
render() {
...
<Candidates setCounter={this.setCounter} />
}
}
The child component Candidates.jsx has another function and thus calls another component:
export class Candidates extends React.Component {
constructor(props) {
super(props);
this.AppProps = props;
}
...
registerVote(name) {
...
this.AppProps.setCounter(name);
}
render() {
...
<MyButton id={this.state.candidates[i].name} register={this.registerVote} />
}
And the last component MyButton.jsx looks like this:
export class MyButton extends React.Component {
constructor(props) {
super();
this.ParentProps = props;
this.state = { active: false }
}
buttonActiveHandler = () => {
this.setState({
active: !this.state.active
});
if (this.state.active === false) {
this.ParentProps.register(this.ParentProps.id);
}
else {
...
}
}
render() {
return (
<Button content='Click here' toggle active={this.state.active} onClick={this.buttonActiveHandler} />
);
}
}
I have successfully debugged that all functions calls are working except when the grandchild MyButton has triggered the registerVote() function in my Candidates module. Logging in this method gets printed but it cannot call this.AppProps.setCounter() from the parent App. I receive the following error:
TypeError: Cannot read property 'setCounter' of undefined
I hope this wasn't too complicated explained, any help is appreciated :)
Simply bind the function in the constructor of the class as #qasimalbaqali stated in his comment.
constructor(props) {
super();
this.registerVote = this.registerVote.bind(this);
}

Accessing/Changing Parents State from Child Class using props (React)

I am trying to set state of the parent class with the child. But having trouble figuring out how to do this. I've abstracted away anything I deemed irrelevant to the question at hand. The issue is that I am
Class Parent extends Component {
constructor(props){
super(props)
this.state = {
foo: "bar"
}
}
coolMethod(n){
this.setState({foo: n})
}
render{
return(
<Child coolmethod={this.coolMethod} />
)
}
}
Class Child extends Component {
constructor(props){
super(props)
}
componentDidMount(){
let that = this;
videojs('my-player', options, function onPlayerReady() {
this.on('end',()=>{
that.props.coolMethod(<whatever string returns as a result of
this method>)
})
})
}
render{
return(
// irrelevant stuff to this question
)
}
}
Currently this code gives me "type error: this.setState is not a function"
If you want more info on videojs: http://videojs.com/ (though this is irrelevant to the question by itself, other than the fact that I reference it in my videojs call in componentDidMount of the child)
I assume the 2nd class is Class Child extends Component .... You need to bind this.coolMethod in your Parent constructor first.
Class Parent extends Component {
constructor(props){
super(props)
this.state = {
foo: "bar"
}
this.coolMethod = this.coolMethod.bind(this);
}
coolMethod(n){
this.setState({foo: n})
}
render{
return(
<Child coolmethod={this.coolMethod} />
)
}
}
Try this, tested working on my side, found two issues in the code
Javascript is case sensitive coolmethod is passed in to the Child, but you are trying to access coolMethod.
You need this > this.coolMethod = this.props.coolMethod.bind(this); in the constructor to inherit the setState function from the Parent, otherwise, this inside the coolMethod will be undefined.
import React, { Component } from 'react';
export default class Parent extends Component {
constructor(props){
super(props)
this.state = {
foo: "bar"
}
}
coolMethod(n){
this.setState({foo: n})
}
render(){
return(
<Child coolMethod={this.coolMethod} />
)
}
}
class Child extends Component {
constructor(props){
super(props)
this.coolMethod = this.props.coolMethod.bind(this);
}
render(){
return(
<button onClick={() => this.coolMethod("aabbcc")}>1</button>
)
}
}

Changing the state of a parent component using a function in the child compontent

I'm new to React, and I decided to build something simple like a Calculator to practice it's basics. However I have some trouble to understand the logic behind the information flow, and either there is a way for a child component to do the logic and update the parent in a natural way.
For example this is the basic structure of my calculator:
class Calculator extends React.Component {
render() {
return (
<div className="calculator-main">
<Screen numberOnScreen={this.state.numberOnScreen}/>
<NumberButton number={7} />
<NumberButton number={8} />
<NumberButton number={9} />
<OperatorButton operator="plus" view="-"/>
....
</div>
)
}
}
class Screen extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="screen">{new Intl.NumberFormat().format(this.props.numberOnScreen)}</div>
);
}
};
class NumberButton extends React.Component {
constructor(props) {
super(props);
}
render() {
const zeroClass = this.props.number === 0 ? " zero" : "";
return (
<button type="button" className={"number" + zeroClass}>{this.props.number}</button>
);
}
};
So I know that:
I can create the functions inside Calculator and pass it as a prop to
the buttons components, and call it onClick. (But it just feel weird).
Create an event listener in the Calculator compontent, create the
function inside the button component and pass the value via the event
trigger; (But it feels artificial).
Use some kind of global store?
But is there no natural react way to do this?
Thanks!
I think you want to know about React component communication. Here, I have implemented Child to Parent communication.
In this case Parent's state and state change method passes to child component through props. Then child can change parent's state use this method.
React Component Communication
//Parent component
class Parent extends React.Component{
constructor(props){
super(props);
this.state = {
content: 'Initial Content'
}
this.changeContent = this.changeContent.bind(this);
}
changeContent(event){
this.setState({
content: event.target.value
})
}
render(){
let { content } = this.state;
return <div>
<Child content={content} changeContent={this.changeContent}/>
<h1>{content}</h1>
</div>
}
}
// Child component
class Child extends React.Component{
constructor(props){
super(props);
}
render(){
let { content, changeContent } = this.props;
return <input value={content} onChange={changeContent}/>
}
}

ReactJS - How to use method from other component of other file? [duplicate]

I have two components. I want to call a method of the first component from the second component. How can I do it?
Here is my code.
First Component
class Header extends React.Component{
constructor(){
super();
}
checkClick(e, notyId){
alert(notyId);
}
}
export default Header;
Second Component
class PopupOver extends React.Component{
constructor(){
super();
// here i need to call Header class function check click....
// How to call Header.checkClick() from this class
}
render(){
return (
<div className="displayinline col-md-12 ">
Hello
</div>
);
}
}
export default PopupOver;
You can do something like this
import React from 'react';
class Header extends React.Component {
constructor() {
super();
}
checkClick(e, notyId) {
alert(notyId);
}
render() {
return (
<PopupOver func ={this.checkClick } />
)
}
};
class PopupOver extends React.Component {
constructor(props) {
super(props);
this.props.func(this, 1234);
}
render() {
return (
<div className="displayinline col-md-12 ">
Hello
</div>
);
}
}
export default Header;
Using statics
var MyComponent = React.createClass({
statics: {
customMethod: function(foo) {
return foo === 'bar';
}
},
render: function() {
}
});
MyComponent.customMethod('bar'); // true
Well, actually, React is not suitable for calling child methods from the parent. Some frameworks, like Cycle.js, allow easily access data both from parent and child, and react to it.
Also, there is a good chance you don't really need it. Consider calling it into existing component, it is much more independent solution. But sometimes you still need it, and then you have few choices:
Pass method down, if it is a child (the easiest one, and it is one of the passed properties)
add events library; in React ecosystem Flux approach is the most known, with Redux library. You separate all events into separated state and actions, and dispatch them from components
if you need to use function from the child in a parent component, you can wrap in a third component, and clone parent with augmented props.
UPD: if you need to share some functionality which doesn't involve any state (like static functions in OOP), then there is no need to contain it inside components. Just declare it separately and invoke when need:
let counter = 0;
function handleInstantiate() {
counter++;
}
constructor(props) {
super(props);
handleInstantiate();
}
You could do this to call a method of the child component from the parent component.
import React from 'react';
class Header extends React.Component {
constructor() {
super();
this.childComponentRef;
}
getChildComponent = (childComponent) => {
this.childComponentRef = childComponent;
this.childComponentRef.sayHi();
}
render() {
return (
<ChildComponent getChildComponent={this.getChildComponent} />
)
}
};
class ChildComponent extends React.Component {
componentDidMount () {
this.props.getChildComponent(this);
}
sayHi = () => {
alert("hi");
}
render() {
return (
<div className="displayinline col-md-12 ">
Hello
</div>
);
}
}
export default Header;

Resources