Access React component functions from outer context - ecmascript-6

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

Related

Pass image link as props

I wanted to pass image link through 2 child components. I put my link inside state and passed it but it's not working. When I manually put link in child component it's working the way it should. What is the matter, can't figure it out.
class App extends React.Component{
state = {
currentImage: 'http://steemitimages.com/640x0/https://steemitimages.com/DQmZ5ZTZGH7odGrk3N7m6xvn2tS4Uz5G2RtkN5A2mM1GZnk/Lucius-Seneca-451x500.png',
}
render(){
return (
<>
<Window
image={this.state.currentImage}
/>
<Form/>
</>
);
}
}
//First Child
class Window extends React.Component {
render() {
return (
<div className={styles.wrapper}>
<div className={styles.content}>
<Quote image={this.props.image}/>
</div>
</div>
)
}
}
//Target child that should display pic
class Quote extends React.Component {
render() {
return(
<div className={styles.wrapper}>
<img
src={this.props.image}
className={styles.image}
/>
</div>
)
}
}
I think you should make a constructor.
class App extends React.Component{
constructor(props) {
super(props);
this.state = {
currentImage: 'https://steemitimages.com/DQmZ5ZTZGH7odGrk3N7m6xvn2tS4Uz5G2RtkN5A2mM1GZnk/Lucius-Seneca-451x500.png'
};
}
render(){
return (
<>
<Window
image={this.state.currentImage}
/>
<Form/>
</>
);
}
}
There is no problem with props.. Just you misused state in App component.

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})

ReactJS- can't read JSON array

My App.jsx file is below.
import React from 'react';
class App extends React.Component {
constructor() {
super();
this.state = {
data:require('json!./dataa.json')
}
}
render() {
return (
<body>
<div>
<Header/>
<center>
<table>
<tr><th>NAME</th><th>VALUE</th><th>COLOR</th><th>Edit Table</th></tr>
<tbody>
{this.state.data.table.map(person, i) => <TableRow key = {i} data = {person} />)}
</tbody></table></center>
</div>
</body>
);
}
}
class Header extends React.Component {
render() {
return (
<div><center>
<h1>Creation of table from JSON</h1></center>
</div>
);
}
}
class TableRow extends React.Component {
render() {
return (
<tr>
<td>{this.props.data.NAME}</td>
<td>{this.props.data.VALUE}</td>
<td>{this.props.data.COLOR}</td>
<td contentEditable='true'></td>
</tr>
);
}
}
export default App;
and my dataa.json file is like below
[{"table":
[{"NAME":"Alan","VALUE":12,"COLOR":"blue"},
{"NAME":"Shan","VALUE":13,"COLOR":"green"},
{"NAME":"John","VALUE":45,"COLOR":"orange"},
{"NAME":"Minna","VALUE":27,"COLOR":"teal"}]
}]
Question: It is compiled fine. but it display error in browser "cannot read the property of map undefined".How to resolve
Note: but it works fine when the json file like,
[{"NAME":"Alan","VALUE":12,"COLOR":"blue"},
{"NAME":"Shan","VALUE":13,"COLOR":"green"},
{"NAME":"John","VALUE":45,"COLOR":"orange"},
{"NAME":"Minna","VALUE":27,"COLOR":"teal"}]
}]
this.state.data doesn't have property table, because it is an array of single object.
Correct JSON structure to this
{
"table": [
{"NAME":"Alan","VALUE":12,"COLOR":"blue"},
{"NAME":"Shan","VALUE":13,"COLOR":"green"},
{"NAME":"John","VALUE":45,"COLOR":"orange"},
{"NAME":"Minna","VALUE":27,"COLOR":"teal"}
]
}
and use this.state.data.table.map.

Invoke function from React component declared as variable

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.

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!