How to load React Component by variable? - reactjs

I have multiple components exported in a file 'buildings.js'
For the sake of simplicity I'll just give a basic example
export const NewBuilding = () => {
return (
<div className="wrapper">New Building</div>
)
}
export const Barracks = () => {
return (
<div className="wrapper">Barracks</div>
)
}
Another component get the specific building component name by props. I need to render the building whose name matches the name in the props.
class BuildingContent extends React.Component {
getComponent = () => {
var name = this.props.content.name;
// Here I want to access the component by variable
// Like <Buildings[name] /> or anything similar that works.
// This obviously doesn't work
//return <Buildings.Name />
return <Buildings.Barracks />
}
render() {
return (
<div className='building-content-wrapper'>
// Hardcoded for functionality
<Buildings.NewBuilding />
</div>
)
}
}

You can create an object of multiple Components and its key should be the name you are passing in props like
const allComponents = {
newBuilding: NewBuilding,
barracks: Barracks,
}
class BuildingContent extends React.Component {
let name = this.props.content.name;
let Building = allComponents[name];
render() {
return (
<div className='building-content-wrapper'>
<Building />
</div>
)
}
}

Related

How to get scroll properties in react-simplebar (in stateful function)

I am new with refs in react.js and in the react-simplebar documentation it just shows how to get the scroll ref for a stateless function.
class App extends React.Component {
render() {
console.log(this.refs.scroll) // => Undefined
return (
<Simplebar ref={this.refs.scroll}><h1>scrollable element</h1></Simplebar>
)
}
}
For anyone coming to this at a later date. This is how I managed to set the scrolling position after a few hours of digging around in a function component
const SimpleScrollerComponent = () => {
// Create a reference for the SimpleBar component so we can acces it
const scrollableNodeRef = React.createRef();
const handleScrollDownBtnClicked = () => {
// This is where we set the scroll position
scrollableNodeRef.current.scrollTop = 1200;
};
return (
<div>
{/* We attach the reference to the component */}
<SimpleBar scrollableNodeProps={{ ref: scrollableNodeRef }}>
{/* This is where your code goes inside the scroll box */}
</SimpleBar>
<Button onClick={handleScrollDownBtnClicked}>Scroll to the Bottom</Button>
</div>
);
};
try this
class App extends React.Component {
constructor(props) {
super(props);
this.scrollableNodeRef = React.createRef();
}
onChangeScrollToTop() {
this.scrollableNodeRef.current.scrollTop = 0;
}
render() {
console.log(this.refs.scroll) // => Undefined
return (
<Simplebar scrollableNodeProps={{ ref:this.scrollableNodeRef }}>
<h1>scrollableelement</h1>
</Simplebar>
)
}
}

TypeError: props.onDeleteClick is not a function

I'm very new to React and I'm trying to make my Delete and Edit button work. But it's giving me this error "TypeError: props.onDeleteClick is not a function"
import React from "react";
import Person from "./Person";
import * as UserService from "../services/userServices";
class People extends React.Component
{
state = {
template: [],
// editperson: ""
};
componentDidMount(){
this.onShowPeople();
}
// Delete Button function
onDeleteClick = myId => {
console.log(myId);
}
onDeleteError = errr => {
console.log(errr)
};
onShowPeople = () => {
UserService
.showAllPeople()
.then(this.onDisplaySuccess)
.catch(this.onDisplayError)
}
onDisplaySuccess = res => {
console.log("Success!")
console.log(res.item.pagedItems)
this.setState({
template: res.item.pagedItems.map(this.renderPersonTemplate)
});
}
onDisplayError = err => console.log("Display Error");
renderPersonTemplate = person => {
return <Person person={person} key={person.id}/>;
}
render()
{
return (
<React.Fragment>
<div className="container">
<div className="template">
{this.state.template}
</div>
</div>
</React.Fragment>
);
}
}
export default People;enter image description here
I just wanted to delete this from my page and from the server.
Your Person component references the following props: onEditClick (line 8), onDeleteClick (line 12), and person (various lines). That means when you render a` you'll need to pass all of those props e.g.
<Person person={...} onEditClick={...} onDeleteClick={...} />
But you are missing the onEditClick and onDeleteClick when you render a <Person> in your People component's renderPersonTemplate function.
You defined an onDeleteClick function on your People component, but that doesn't magically get passed anywhere. You need to explicitly do that yourself. Also, you need to define an onEditClick function somewhere so you can pass that too.
class People {
/* ... existing code ... */
// Delete Button function
onDeleteClick = myId => {
console.log('deleting', myId);
}
// add this Edit Button function
onEditClick = myId => {
console.log('editing', myId);
}
renderPersonTemplate = person => {
return <Person
key={person.id}
person={person}
onEditClick={this.onEditClick} // add this
onDeleteClick={this.onDeleteClick} // add this
/>;
}
/* ... */
}
The reason you got the type error is because you weren't passing any onDeleteClick with the <Person>, so inside the Person function, props.onDeleteClick was undefined instead of being a function as your code expected.

How to change attribute of a React Element

I've created a render method which adds a number of dynamically created 'InfoWindow' elements to a Class based object.
Each InfoWindow element has a unique ID and key.
I also have a number of 'Marker' elements with corresponding ids and keys.
Currently all Infowindows have a prop of 'visible={false}'
When I click a Marker a function is called which outputs the Marker ID.
I'd like to find the InfoWindow with the relevant ID and set visibility = {true}
Is there a way to find the relevant InfoWindow element using its key or ID, and then call setAttribute (or equivalent)?
I've tried searching the DOM for the ID but Google Maps doesn't render that way, so I'm thinking there must be a more React-y way to do this?
let visibilityFunction = () => {
this.changeVisibility(01);
};
changeVisibility = (e) => {
console.log(e);
//this currently outputs the ID (01)
}
render() {
return(
<Parent>
<InfoWindow
visible={false}
key={01-iw}
id={01-iw}
/>
<Marker
key={01}
id={01}
onClick={visibilityFunction}
/>
</Parent>
);
}
Like I was saying in the comments. Use state here to update the visibility.
class MyComponent extends React.Component {
state = { visibleWindows: {}, currentWindows: [1] };
changeVisibility = id => {
this.setState(prevState => ({
visibleWindows: {
...prevState.visibleWindows,
[id]: !prevState.visibleWindows[id]
}
}));
};
render() {
const { currentWindows, visibleWindows } = this.state;
return (
<div>
{currentWindows.map(win => (
<ChildWindow key={win} id={win} isVisible={!!visibleWindows[win]} onChange={this.changeVisibility} />
))}
</div>
);
}
}
class ChildWindow extends React.Component {
changeVisibility = () => {
this.props.onChange(this.props.id)
}
render() {
<React.Fragment>
<InfoWindow
visible={this.props.isVisible}
key={`${win}-iw`}
id={`${win}-iw`}
/>
<Marker
key={win}
id={win}
onClick={this.changeVisibility}
/>
</React.Fragment>
}
}
Here's a rudimetary example for you to poke around with :)

ReactJS: TypeError: this.ref.current.method is not a function with ant design form

class Parent extends Component {
constructor(props) {
super(props);
this.Child_A = React.createRef();
this.Child_B = React.createRef();
}
function_uses_Child_A = ()=> {
// This is working fine
this.Child_A.current.Child_A_Function()
}
function_uses_Child_B = ()=> {
// This is Does NOT work
// this.Child_A.current.Child_B_Function() is not a function
this.Child_A.current.Child_B_Function()
}
render() {
return (
<div>
<Child_A ref={this.Child_A}/>
<Child_B ref={this.Child_B}/>
</div>
);
}
}
export default Parent;
The above code shows my problem where both has the same code but one works and the other doesn't
This is Child A component:
class Child_A extends Component {
Child_A_Function = () => "Working";
render = () => <h1>Child_A</h1>
}
export default Child_A;
This is Child B component:
import {Form} from "antd";
class Child_B extends Component {
Child_B_Function = () => "Not Working";
render = () => <h1>Child_B</h1>
}
export default Form.create()(Child_B);
I tried to debug this.Child_B.current
image debug info
I believe it shows the Form.create() data and removing mine
I understand this because Child_A works fine and the only different is it doesn't have Form.create()
This is because Form.create()() is a higher order function which returns another component.
so
const DecoratedChild_B = Form.create()(Child_B);
DecoratedChild_B may have other wrapper around it, and it become like this:
<wrapper ref={this.Child_B}>
<Child_B/>
</wrapper>
That's why you don't get what you want.
to get form ref you should use wrappedComponentRef
const EnhancedForm = createForm()(Form);
<EnhancedForm wrappedComponentRef={(inst) => this.formRef = inst} />
this.formRef // => The instance of Form
if you want something custom, you have to use other name for the ref func

Can I add two proptypes together after they are rendered? If so, how can I accomplish that?

()I have a div with a prop that I would like to display based on whether a prop is bigger in number than another prop. I have a lot going on in this particular component and I'm concerned that all of the following things I'm trying to do are not possible.
this.props.currentValue < this.props.newValue is not working for me, but everything else is working just fine.
I'm very new to React. Any help would be awesome!
Oh, and the value of currentValue and newValue are inside of the rates component on a separate page.
import React, {PropTypes, Component} from 'react';
import Header from '../compare-table-header/compare-table-header';
import './compare-table-row.css';
export class Rates extends Component {
constructor(props) {
super(props);
this.displayThing = this.displayThing.bind(this);
}
displayThing() {
const increase = <div>{this.props.details}</div>;
const thing = <div>hi</div>;
if (this.props.currentValue < this.props.newValue) {
return increase;
} else {
return thing;
}
}
render() {
const {currentValue, newValue} = this.props;
return (
<div>
<Header heading="Rates" />
<div className="value-heading">{currentValue}</div>
<div className="value-heading">{newValue}</div>
</div>
<div>{this.displayThing()}</div>
</div>
</div>
</div>
);
}
}
Rates.propTypes = {
currentValue: PropTypes.number,
newValue: PropTypes.number
};
The line with this.displayThing isn't rendering anything because you're passing a reference to the function itself, instead of calling the function and rendering the value it returns.
That line should do what you expect if you change this.displayThing to this.displayThing().
But you also have some mismatched tags. The Header component is opened and closed on the same line. From your indentation, it looks like you meant for the lines below it to be rendered as children of the Header component, but that's not what's actually happening.
You could clean that up like this:
return (
<div>
<Header heading="Rates">
<div className="value-heading">{currentValue}</div>
<div className="value-heading">{newValue}</div>
</Header>
<div>{this.displayThing()}</div>
</div>
);
Or, if your Header component doesn't render any children, that might look like this:
return (
<div>
<Header heading="Rates" />
<div className="value-heading">{currentValue}</div>
<div className="value-heading">{newValue}</div>
<div>{this.displayThing()}</div>
</div>
);
If you want to go a little further, you can also remove some code and simplify the class a little by defining the displayThing function as an arrow function:
Instead of this:
export class Rates extends Component {
constructor(props) {
super(props);
this.displayThing = this.displayThing.bind(this);
}
displayThing() {
const increase = <div>{this.props.details}</div>;
const thing = <div>hi</div>;
if (this.props.currentValue < this.props.newValue) {
return increase;
} else {
return thing;
}
}
// ... rest of the class
}
you can make displayThing into an arrow function and get rid of the constructor, like this:
export class Rates extends Component {
displayThing = () => {
const increase = <div>{this.props.details}</div>;
const thing = <div>hi</div>;
if (this.props.currentValue < this.props.newValue) {
return increase;
} else {
return thing;
}
}
// ... rest of the class
}
The class works the same either way, but it saves a few lines of code.

Resources