Invoke function from React component declared as variable - ecmascript-6

How to invoke React component's function when this component is given in variable? I have a Parent that passes Test class into Child component, and this child wants to change something in Test.
export class Parent extends React.Component {
render() {
let test = (<Test />);
return (<Child tester={test} />);
}
}
export class Child extends React.Component {
render() {
this.props.tester.setText("qwerty"); // how to invoke setText, setState or something like that?
return ({this.props.tester});
}
}
export class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
text: this.props.text || ""
};
}
setText(text) {
this.setState({ text: text });
}
render() {
return (<div>{this.state.text}</div>);
}
}

I think you should think about life cycle of react components.
Please try the code below(I just added logging), and observe logs carefully.
export class Parent extends React.Component {
render() {
let test = (<Test />);
return (<Child tester={test} />);
}
}
export class Child extends React.Component {
render() {
console.log("Child render"); // <= logging added!
// this.props.tester.setText("qwerty");
// What kind of object is 'this.props.tester(= <Test />)' here???
return ({this.props.tester});
}
}
export class Test extends React.Component {
constructor(props) {
super(props);
console.log("Test constructor"); // <= logging added!
this.state = {
text: this.props.text || ""
};
}
setText(text) {
// this.setState({ text: text });
// this is another problem. We cannot call setState before mounted.
this.state.text= text;
}
render() {
return (<div>{this.state.text}</div>);
}
}
If so, you will see 2 important facts.
'Test' component is not instantiated yet, when you call 'setText'.
How can we call a method of object which is not instantiated? Cannot!
this means 'this.props.tester' is not an instance of 'Test' component.
But if you really want to exec your code, modify Child.render like this.
render() {
var test = new Test({props:{}});
// or even this can work, but I don't know this is right thing
// var test = new this.props.tester.type({props:{}});
test.setText("qwerty");
return test.render();
}
But I don't think this is a good way.
From another point of view, one may come up with an idea like,
render() {
// Here, this.props.tester == <Test />
this.props.tester.props.text = "qwerty";
return (this.props.tester);
}
but of course it's not possible, because 'this.props.tester' is read-only property for Child.

Related

How can I get a width of html element in React JS?

I need to get a width of html element, using React JS. When I do console.log(this.widthPromoLine) in componentDidMount(), it works, but when I do this.setState({moveContent: this.widthPromoLine}), it doesn't.
import React from 'react'
import './index.css'
class Promo extends React.Component {
constructor(props){
super(props)
this.state = {
moveContent: 0
}
}
componentDidMount(){
this.setState({moveContent: this.widthPromoLine})
}
render(){
return <div
className="promo-content"
ref={promoLine => this.widthPromoLine = promoLine.clientWidth}>
</div>
}
}
.promo-content {
width: 1870px;
}
Access the clientWidth after the ref has been assigned to the variable this.widthPromoLine in componentDidMount and then set it like below. Also setState is async, so you need to do anything after the state has been updated in the callback of setState.
import React from "react";
import "./styles.css";
class Promo extends React.Component {
constructor(props) {
super(props);
this.state = {
moveContent: 0
};
}
componentDidMount() {
this.setState({ moveContent: this.widthPromoLine.clientWidth }, () => {
console.log(this.state);
});
}
render() {
return (
<div
className="promo-content"
ref={promoLine => (this.widthPromoLine = promoLine)}
/>
);
}
}
Hope this helps !
You can get the width of content using it classname as follow.
let width = document.querySelector(".promo-content").offsetWidth;
And after that update the state,
this.setState({moveContent: width})

REST calls from a react component

I'm trying to replicate same code here with different JSON, but the data is not loading.
Please help, I'm not sure what is missing in the code.
import React from 'react';
export default class ItemLister extends React.Component {
constructor() {
super();
this.state = { items: [] };
}
componentDidMount() {
fetch('http://media.astropublications.com.my/api/drebar_landing.json')
.then(result=>result.json())
.then(items=>this.setState({items}));
}
render() {
return(
<ul>
{this.state.items.length ?
this.state.items.map(item=><li key={item.id}>{item.Title}</li>)
: <li>Loading...</li>
}
</ul>
)
}
}
Your api response contains an object ArticleObject and the ArticleObject has array of objects so you need to set the items.ArticleObject to the state.
Take a look at below solution for better understanding
componentDidMount() {
fetch('http://media.astropublications.com.my/api/drebar_landing.json')
.then(result=>result.json())
.then(items=>this.setState({items:items.ArticleObject}));
}

React Native, Navigating a prop from one component to another

handleShowMatchFacts = id => {
// console.log('match', id)
return fetch(`http://api.football-api.com/2.0/matches/${id}?Authorization=565ec012251f932ea4000001fa542ae9d994470e73fdb314a8a56d76`)
.then(res => {
// console.log('match facts', matchFacts)
this.props.navigator.push({
title: 'Match',
component: MatchPage,
passProps: {matchInfo: res}
})
// console.log(res)
})
}
I have this function above, that i want to send matchInfo to matchPage.
I take in that prop as follows below.
'use strict'
import React from 'react'
import { StyleSheet, View, Component, Text, TabBarIOS } from 'react-native'
import Welcome from './welcome.js'
import More from './more.js'
export default class MatchPage extends React.Component {
constructor(props) {
super(props);
}
componentWillMount(){
console.log('mathc facts ' + this.props.matchInfo._bodyInit)
}
render(){
return (
<View>
</View>
)
}
}
All the info I need is in that object - 'this.props.matchInfo._bodyInit'. My problem is that after '._bodyInt', I'm not sure what to put after that. I've tried .id, .venue, and .events, they all console logged as undefined...
You never change props directly in React. You must always change the state via setState and pass state to components as props. This allows React to manage state for you rather than calling things manually.
In the result of your api call, set the component state:
this.setState({
title: 'Match',
component: MatchPage,
matchInfo: res
}
Then pass the state as needed into child components.
render() {
return(
<FooComponent title={this.state.title} matchInfo={this.state.matchInfo} />
);
}
These can then be referenced in the child component as props:
class FooComponent extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
console.log(this.props.title);
console.log(this.props.matchInfo);
// Etc.
}
}
If you need to reference these values inside the component itself, reference state rather than props.
this.state.title;
this.state.matchInfo;
Remember components manage their own state and pass that state as props to children as needed.
assuming you are receiving json object as response , you would need to parse the response before fetching the values.
var resp = JSON.parse(matchInfo);
body = resp['_bodyInit'];

Access React component functions from outer context

I have Class1 which is only a container for Class2. I declare Test component in Class1. Now I want to pass Test into Class2 as a parameter.
Is it possible to access Test component's context being inside Class2 in place where I put a comment in?
export default class Class1 extends React.Component {
render() {
let test = (<Test />);
return (
<Class2 test={test} />
);
}
}
export default class Class2 extends React.Component {
constructor(props) {
super(props);
}
render() {
let { test } = this.props;
// how to access Test class context?
test.setValue("WHERE IS MY CONTEXT?");
return (
<div>{ test }</div>
);
}
}
export default class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
value: ""
};
}
setValue(val) {
this.setState({
value: val
});
}
render() {
return (
<div>{ this.state.value }</div>
);
}
}
I tried to find something on Web and to examine the test object, but I found nothing... When I try to access test.props directly I get a React error that props are read only and can't be accessed...
Use props instead of state:
let { test } = this.props;
<div>{ React.cloneElement(test, {value: "Hello World"}) }</div>
And in Test:
<div>{ this.props.value }</div>
PS: Context means something else in React.
A complete example:
class Class1 extends React.Component {
render() {
return (
<Class2>
<Test />
</Class2>
);
}
}
class Class2 extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
{React.cloneElement(this.props.children, {text: "WHERE IS MY CONTEXT?"})}
</div>
);
}
}
class Test extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>{ this.props.text }</div>
);
}
}
JSFiddle

Dynamic templates in React

I want to create a simple Wizard component that should be as generic as possible.
I want to inject 2 params for body content: template (with some logic included) and its context.
export class ParentClass extends React.Component {
render() {
let template = `Some text: {this.context.testFunc()}`;
let context = new TestContext();
return (
<Wizard template={template} context={context} />
);
}
}
export class TestContext {
testFunc() {
return "another text";
}
}
export class Wizard extends React.Component {
context: null;
constructor(props) {
super(props);
this.context = this.props.context;
}
render() {
return (
<div>
{this.props.template}
</div>
);
}
}
The problem is the logic included in template does not execute (it writes everything as a string in Wizard).
I use ES2015 and Babel for compiling.
When you are using template literals you have to use $.
For example
`Some text: {this.context.testFunc()}`;
should be
`Some text: ${this.context.testFunc()}`;
Also, I think that you got a problem in your render function
render() {
let template = `Some text: {this.context.testFunc()}`;
let context = new TestContext();
return (
<Wizard template={template} context={context} />
);
}
should be
render() {
let context = new TestContext();
let template = `Some text: ${context.testFunc()}`;
return (
<Wizard template={template} context={context} />
);
}
Hope it helps!