Rendering objects dynamically with KonvaJs-ReactJs - javascript

I want to render an array of React components with the help of KonvaJs without knowing exactly which object I draw at a particular moment. To be more specific, here is my code:
One of the React components I want to render, wall.js:
class CWall extends React.Component {
render() {
return (
<React.Fragment>
<Rect /*some props*/></Rect>
<Transformer /*some props*/></Transformer>
</React.Fragment>
)
}
}
In the other component I create CWall when the button is clicked, planmenu.js:
class PlanMenu extends React.Component {
render() {
...
return (
...
<button type="button"
onClick={() => { addObject(
new CWall({
x: 100,
y: 100,
length: 200
}))}}>Wall
</button>
)
}
}
The created objects are passed to the component which should display them, planbuilder.js:
import CWall from './objects/wall'
class PlanBuilder extends React.Component {
render() {
const { objects } = this.props
return (
<Stage>
<Layer>
{
objects.map(function(object) {
var ObjectType = object.constructor.name;
/* the following line gives an error to me */
return <ObjectType {...object.props} key={object.id} />;
}, this)
}
</Layer>
</Stage>
);
}
}
The specified line throws an error:
konva has no node with the type CWall
However, if I render one CWall directly, I get it on the screen as expected. It seems to me like evidence that konva is able to render CWall objects:
class PlanBuilder extends React.Component {
render() {
const { objects } = this.props
return (
<Stage>
<Layer>
<CWall x={100} y={100} length={200} />
</Layer>
</Stage>
);
}
}
So my question is: what is the proper way of rendering objects without knowing their exact types?
Thank you in advance.

In general, you should not add React components directly into the state. Instead, just add pure data about your app, and then just render from that data. It can be like this:
class PlanMenu extends React.Component {
render() {
...
return (
...
<button type="button"
onClick={() => { addObject({ x: 10, y: 10, type: 'wall' })}}
</button>
)
}
}
import CWall from './objects/wall'
const TYPES = {
'wall' : CWall
};
class PlanBuilder extends React.Component {
render() {
const { objects } = this.props
return (
<Stage>
<Layer>
{
objects.map(function(object) {
const Component = TYPES[object.type];
return <Component {...object} key={object.id} />;
}, this)
}
</Layer>
</Stage>
);
}
}

You can use array of JSX.Element, when button clicks you will create JSX.Element and push it to array of JSX.Element and then put array into render for example
<button type="button"
onClick={() => { addObject(
<CWall
x: 100,
y: 100,
length: 200
/>)}
</button>
render() {
const { objects } = this.props
return (
<Stage>
<Layer>
{objects}
</Layer>
</Stage>
);
}

Related

I want to create some program with react js, about adding strings in window, but I am having an error

I am creating a program with React Js, where I need to write something in prompts and it needs to appear in window. Now I have created the function of adding prompts and pasting it in window, but it's giving an error.
please help me if you can : )
export default class Clock extends React.Component {
state = {items: ['Hakob', 'Arman']};
Add(){
const newitems = this.state.items.concat([prompt('a')])
this.setState({items:newitems})
}
render(){
return <div>
<Clock2/>
</div>
}
}
class Clock2 extends React.Component {
render(){
return(
<>
<button onClick={this.Add}>click</button>
<div> {this.state.items.map((e, i) => {
return <div key = {e + i}> {e} </div>
} )} </div>
</>
)
}
}
you have not defined any state in class clock2 so, the line # 798 giving you an error for cannot read property of items as it is not defined in class
class Clock2 extends React.Components {
state = {
items : //
}
}
and the second error is you are trying to return in the return function that is not correct if you want to map items you have to define map function in render
{
const items = this.state.items.map((e,i ) => {
//
}
return (
<items/>
)
So let's write your code here.
export default class Clock extends React.Component {
state = { items: ['Hakob', 'Aram']};
Add() {
const newItems = this.state.items.concat([prompt('a')])
this.setState({items:newItems})
}
render() {
return <div><Clock2/><div>
}
}
class Clock2 extends React.Component {
render() {
return (
<>
<button onClick={this.Add}>Click</button>
<div>{ this.state.items.map( (e,i) => {
return <div key={ e + i}>{e}</div>
})}
</div>
</>
)
}
}
You made a mistake, state are internals to component, as well as method.
This should work
export default class Clock extends React.Component {
state = { items: ['Hakob', 'Aram']};
Add() {
const newItems = this.state.items.concat([prompt('a')])
this.setState({items:newItems})
}
render() {
return (
<>
<button onClick={this.Add}>Click</button>
<div>{ this.state.items.map( (e,i) => {
return <div key={ e + i}>{e}</div>
})}
</div>
</>
)
}
}
Or you can pass values from top component to his child.
export default class Clock extends React.Component {
state = { items: ['Hakob', 'Aram']};
Add() {
const newItems = this.state.items.concat([prompt('a')])
this.setState({items:newItems})
}
render() {
return (<div><Clock2 add={this.Add} values={this.state.items}/><div>)
}
}
class Clock2 extends React.Component {
render() {
return (
<>
<button onClick={this.prop.add}>Click</button>
<div>{ this.props.values.map( (e,i) => {
return <div key={ e + i}>{e}</div>
})}
</div>
</>
)
}
}

The proper way of exchanging objects between sibling components in React

I am trying to build a simple React app and stuck into trouble. I have the following structure:
App.js
class App extends React.Component {
render() {
return (
<div className="App">
<PlanBuilder />
<PlanMenu />
</div>
);
}
}
PlanMenu.js
class PlanMenu extends React.Component {
render() {
return (
<div className="PlanMenu">
<button type="button"
onClick={addObject(
new CWall({
x: 100,
y: 100,
length: 200
}))}>Wall
</button>
</div>
);
}
}
PlanBuilder.js
class PlanBuilder extends React.Component {
constructor(props) {
super(props);
this.state = {
objects: []
};
}
addObject(object) {
this.setState({
objects: [...this.state.objects, object]
});
}
render() {
return (
<Stage>
<Layer>
{
this.state.objects.map(function(object) {
return object.render();
})
}
</Layer>
</Stage>
);
}
So the main idea is that I have two sibling components: the drawing area and the menu. When the button on the menu is pressed, I want to create a new object and send it to the drawing area element. So, the question is how to pass PlanBuilder.addObject method to the PlanMenu class. I came from the C world, and what I think about is to pass kinda function pointer to PlanMenu. However, I am not sure this is an appropriate solution. Would you please recommend me the proper way of doing this in React? Thank you in advance.
In this case you have two ways.
The simpler one is to move the logic you have on PlanBuilder to App, and pass the necessary props to PlanBuilder and PlanMenu, like:
class PlanMenu extends React.Component {
render() {
const { addObject } = this.props
return (
<div className="PlanMenu">
<button type="button"
onClick={addObject(
new CWall({
x: 100,
y: 100,
length: 200
}))}>Wall
</button>
</div>
);
}
}
class PlanBuilder extends React.Component {
render() {
const { objects } = this.props
return (
<Stage>
<Layer>
{objects.map(function(object) {
return object.render();
})}
</Layer>
</Stage>
)
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
objects: []
};
this.addObject = this.addObject.bind(this)
}
addObject(object) {
this.setState({
objects: [...this.state.objects, object]
});
}
render() {
const { objects } = this.state
return (
<div className="App">
<PlanBuilder objects={objects} />
<PlanMenu addObject={this.addObject} />
</div>
);
}
}
The other alternative is to create a "Container" to hold the logic instead adding it to App, like:
class PlanMenu extends React.Component {
render() {
const { addObject } = this.props
return (
<div className="PlanMenu">
<button type="button"
onClick={addObject(
new CWall({
x: 100,
y: 100,
length: 200
}))}>Wall
</button>
</div>
);
}
}
class PlanBuilder extends React.Component {
render() {
const { objects } = this.props
return (
<Stage>
<Layer>
{objects.map(function(object) {
return object.render();
})}
</Layer>
</Stage>
)
}
}
class PlanContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
objects: []
};
this.addObject = this.addObject.bind(this)
}
addObject(object) {
this.setState({
objects: [...this.state.objects, object]
});
}
render() {
const { objects } = this.state
return (
<div>
<PlanBuilder objects={objects} />
<PlanMenu addObject={this.addObject} />
</div>
)
}
}
class App extends React.Component {
render() {
return (
<div className="App">
<PlanContainer />
</div>
);
}
}
In my opinion, creating a Container makes your code more readable, reusable and cleaner :)
Hope it help!

React Native - How to append to parent state array while in child component (StackNavigators)?

My project is looping through a data array in a child component Main, and I'm trying to update the state in parent component, App, on an event (swiping right on a card in Main), so that I could access the data that was 'swiped right' on a sibling Component in Favorites. Hopefully that makes sense?
The project structure is as such:
App
|__ Rootstack
|
|__Favorites
|__Main
In my Main component, I am mapping the collection array and looping thru:
collection = imagedata;
// a local JSON array of data that I am looping thru in Main
class Main extends React.Component {
_toFavs = () => {
this.props.navigation.navigate('Favorites');
};
render() {
const contents = collection.map((item, index) => {
return (
<Card key={index}>
......
</Card>
)
});
return (
<View>
<CardStack
onSwiped={() => {console.log('onSwiped')}
onSwipedRight={() => console.log('onSwipedLeft')}>
//
//HERE IS THE PART - HOW TO UPDATE THE 'favoritesList' array in the parent 'App's state?
//
{contents}
</CardStack>
</View>
);
}
}
const RootStack = StackNavigator(
{
Main: {
screen: Main},
Favorites: {
screen: Favorites}
},
{
initialRouteName: 'Main'
}
);
class Favorites extends React.Component {
// The plan is to eventually access the favoritesList array in App's state here and display cards that were swiped right in the Main component.
_onPress = () => {
this.props.navigation.navigate('Main');
};
render() {
return (
<View><Text>Hello!</Text></View>
);
}
}
export default class App extends Component<{}> {
constructor(props) {
super(props);
this.state = {
favoritesList: []
};
}
render() {
return <RootStack />;
}
}
I've come across some other answers of updating state such as
this.setState({ favoritesList: [...this.state.favoritesList, 'new value'] }), but how can I do this to the .state of App while i'm inside a child component Main?
Thanks in advance!
collection = imagedata;
// a local JSON array of data that I am looping thru in Main
class Main extends React.Component {
_toFavs = () => {
this.props.navigation.navigate('Favorites');
};
render() {
const contents = collection.map((item, index) => {
return (
<Card key={index}>
......
</Card>
)
});
return (
<View>
<CardStack
onSwiped={() => {console.log('onSwiped')}
onSwipedRight={() => {console.log('onSwipedLeft') ;
this.props.screenProps()}}>
//
{contents}
</CardStack>
</View>
);
}
}
const RootStack = StackNavigator(
{
Main: {
screen: Main},
Favorites: {
screen: Favorites}
},
{
initialRouteName: 'Main'
}
);
class Favorites extends React.Component {
// The plan is to eventually access the favoritesList array in App's state here and display cards that were swiped right in the Main component.
_onPress = () => {
this.props.navigation.navigate('Main');
};
render() {
return (
<View><Text>Hello!</Text></View>
);
}
}
export default class App extends Component<{}> {
constructor(props) {
super(props);
this.state = {
favoritesList: []
};
}
updateArr=()=>{consol.log("fire") }
render() {
return <RootStack screenProps={this.updateArr} />;
}
}
i hope it solve your problem
update props-name

React.Children.only expected to receive a single React element child error while using Ref

I have to call some methods from component B inside component A.
Here is the component B: (In the case that you need more code just request me and I will update my post)
import A from "./A";
export default class B extends Component {
componentDidUpdate (prevProps, prevState) {
this.props.onRef(this);
}
renderContent ( ) {
let toolbarActiveItem = Toolbar.getActiveItem();
if (Toolbar.getActiveItem()=="XXXX") {
this.refs.A._showModalHistory();
} else {
toolbarActiveItem = Toolbar.getActiveItem();
}
return (
<Container>
{(toolbarActiveItem == 'YYY') && <C navigation={this.props.navigation} cb={this.childCallback} info={this.props.navigation.state.params}/> }
</Container>
);
}
render () {
return (
<StyleProvider style={getTheme(Config.theme)}>
<A ref={component => { this.A = component; }} />
<Container ref={this.ref}>
{ this.renderNavbar(this.title) }
{ this.renderToolbar() }
{ this.renderContent() }
{ this.renderDialog() }
</Container>
</StyleProvider>
);
}
Here is the component A:
export default class A extends Component {
constructor(props) {
super();
this.subscription = 0;
this.state = {};
changePage = props.cb;
this.state = {
isModalVisibleHistory: false,
};
}
_showModalHistory = () => this.setState({ isModalVisibleHistory: true });
render() {
return (
<Modal isVisible={this.state.isModalVisibleHistory}>
<View style={styles.BG}>
<ParkHistory></ParkHistory>
</View>
</Modal>
);
}
}
My problem is that, I need to execute this.refs.A._showModalHistory(); in component B but, I see the following error:
React.Children.only expected to receive a single React element child.
I think, I have a problem to deal with rendering multiple components and ref them. I should mention that, when I delete <A ref={component => { this.A = component; }} /> my code works fine but, without calling the method. Could you please help me to solve this issue?
The problem is in the way you are accessing the ref. You have assigned ref to Component A. You are using the callback style to assign ref and so you need not write this.ref.A. Change it to this.A._showModalHistory();
Aslo the way you are setting ref to Container component is incorrect, you need to use callback there
<Container ref={(ref)=> this.ref = ref}>
One other thing is that the StyleProvider component expects a single child element. You should wrap your Container and A component in View
render () {
return (
<StyleProvider style={getTheme(Config.theme)}>
<View>
<A ref={component => { this.A = component; }} />
<Container ref={(ref)=> this.ref = ref}>
{ this.renderNavbar(this.title) }
{ this.renderToolbar() }
{ this.renderContent() }
{ this.renderDialog() }
</Container>
</View>
</StyleProvider>
);
}

React.JS - multiple elements sharing a state ( How do I modify only one of the elements without affecting the others? )

class App extends Component {
constructor(props) {
super(props);
this.state = { Card: Card }
}
HandleEvent = (props) => {
this.SetState({Card: Card.Active}
}
render() {
return (
<Card Card = { this.state.Card } HandleEvent={
this.handleEvent }/>
<Card Card = { this.state.Card } HandleEvent={
this.handleEvent }/>
)
}
}
const Card = props => {
return (
<div style={props.state.Card} onClick={
props.HandleEvent}>Example</div>
)
}
Every time I click on one of the cards all of my elements change states, how do I program this to only change card that I clicked?
Here's a working example
import React, { Component } from 'react'
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
0: false,
1: false
};
}
handleEvent(idx) {
const val = !this.state[idx];
this.setState({[idx]: val});
}
render() {
return (
<div>
<Card state={this.state[0]} handleEvent={()=>this.handleEvent(0) } />
<Card state={this.state[1]} handleEvent={()=>this.handleEvent(1) } />
</div>
);
}
}
const Card = (props) => {
return (<div onClick={() => props.handleEvent()}>state: {props.state.toString()}</div>);
}
You can also see it in action here
Obviously this is a contrived example, based on your code, in real world application you wouldn't store hardcoded state like {1: true, 2: false}, but it shows the concept
It's not completely clear from the example what is the Card in the constructor. But here the example of how you can modify clicked element.
Basically you can keep only index of clicked element in parent's state, and then pass it as some property to child component, i.e. isActive here:
const cards = [...arrayOfCards];
class App extends Component {
constructor(props) {
super(props);
this.state = { activeCardIndex: undefined }
}
HandleEvent = (index) => {
this.SetState({
activeCardIndex: index
});
}
render() {
return ({
// cards must be iterable
cards.map((card, index) => {
return (
<Card
key={index}
Card={Card}
isActive={i === this.state.activeCardIndex}
HandleEvent={this.HandleEvent.bind(this, index)}
/>
);
})
});
}
}
const Card = props => {
// style active card
const style = Object.assign({}, props.Card, {
backgroundColor: props.isActive ? 'orange' : 'white',
});
return (
<div style={style} onClick={
props.HandleEvent}>Example</div>
)
}

Categories

Resources