I am trying to render a react component. The first part of the component will remain the same, however the second part needs to change dynamically based on the data returned from the props. Is there some way to concatenate these together?
import * as Icon from 'react-cryptocoins';
class ResultRow extends PureComponent {
render() {
return (
<div className="component-result-row">
<Icon.{this.props.name}/>
</div>
);}}
Hardcoding the name works as expected
<{Icon.image1}/>
Solution:
import * as Icon from 'react-cryptocoins';
class ResultRow extends PureComponent {
var name = this.props.name;
var Component = Icon[symbol];
render() {
return (
<div className="component-result-row">
<Component/>
</div>
);}}
One possible way is store the value in a variable (should start with capital letter) then render that.
Like this:
class ResultRow extends PureComponent {
render() {
const Comp = Icon[this.props.name];
return (
<div className="component-result-row">
<Comp />
</div>
);
}}
Related
Background
I wrote an exact, short yet complete example of a Parent component with a nested Child component which simply attempts:
Alter a string in the Parent's state
See the Child component updated when the Parent's state value is altered (this.state.name)
Here's What It Looks Like
When the app loads a default value is passed from Parent state to child props.
Change The Name
All I want to do is allow the change of the name after the user adds a new name in the Parent's <input> and clicks the Parent's <button>
However, as you can see, when the user clicks the button only the Parent is rendered again.
Questions
Is it possible to get the Child to render the new value?
What am i doing wrong in this example -- why isn't it updating or
rendering the new value?
All Source Code
Here is all of the source code and you can view it and try it in my StackBlitz project.
I've kept it as simple as possible.
Parent component (DataLoader)
import * as React from 'react';
import { useState } from 'react';
import { Grid } from './Grid.tsx';
interface LoaderProps {
name: string;
}
export class DataLoader extends React.Component<LoaderProps, {}> {
state: any = {};
constructor(props: LoaderProps) {
super(props);
this.state.name = this.props.name;
this.changeName = this.changeName.bind(this);
}
render() {
const { name } = this.state;
let parentOutput = <span>{name}</span>;
return (
<div>
<button onClick={this.changeName}>Change Name</button>
<input id="mapvalue" type="text" placeholder="name" />
<hr id="parent" />
<div>### Parent ###</div>
<strong>Name</strong>: {parentOutput}
<hr id="child" />
<Grid childName={name} />
</div>
);
}
changeName() {
let newValue = document.querySelector('#mapvalue').value.toString();
console.log(newValue);
this.setState({
name: newValue,
});
}
}
Child component (Grid)
import * as React from 'react';
interface PropsParams {
childName: string;
}
export class Grid extends React.Component<PropsParams, {}> {
state: any = {};
constructor(props: PropsParams) {
super(props);
let counter = 0;
this.state = { childName: this.props.childName };
console.log(`CHILD -> this.state.name : ${this.state.childName}`);
}
render() {
const { childName } = this.state;
let mainChildOutput = <span>{childName}</span>;
return (
<div>
<div>### Child ####</div>
<strong>Name</strong>: {mainChildOutput}
</div>
);
}
}
App.tsx is set up like the following -- this is where default value comes in on props
import * as React from 'react';
import { DataLoader } from './DataLoader.tsx';
import './style.css';
export default function App() {
return (
<div>
<DataLoader name={'default value'} />
</div>
);
}
You're seeing two different values because you're tracking two different states. One in the parent component and one in the child component.
Don't duplicate data.
If the child component should always display the prop that's passed to it then don't track state in the child component, just display the prop that's passed to it. For example:
export class Grid extends React.Component<PropsParams, {}> {
render() {
const { childName } = this.props; // <--- read the value from props, not local state
let mainChildOutput = <span>{childName}</span>;
return (
<div>
<div>### Child ####</div>
<strong>Name</strong>: {mainChildOutput}
</div>
);
}
}
In the Child component, you set the prop childName value to state in the contructor ONLY. The constructor is executed ONLY WHEN THE COMPONENT IS MOUNTED. So, it doesn't know if the childName prop is changed later.
There are 2 solutions for this.
(1) Directly use this.props.childName without setting it to a state.
(2) Add a useEffect that updates the state value on prop change.
React.useEffect(() => {
this.state = {
childName: this.props.childName;
};
}, [this.props.childName]);
However, I recommend 1st solution since it's not a good practice to duplicate data.
I am a bit of a react newbie so please be gentle on me. I am trying to build an education app, and code is to render multiple choice answer boxes:
export default function AnswerBox(props: any) {
return (
<div className="answer-container">
<ul>
{props.answers.map((value: any) => {
return (
<li className="answer-box" key={value.letter} id={value.letter}>
<input className="answer-textbox" type="checkbox" onChange={() => console.log('selected: ', value.letter)}></input>
<span className="answer-letter"><b>{value.letter})</b></span>
{value.answer}
</li>)
})
}
</ul>
</div>
)
}
As you can see, the function takes a object of arrays and iterates through arrays to display the Question Letter (e.x. 'A') and Question Answer in an unordered list.
So all of this is good, but I'd like the list element to be highlighted or have the answerbox div changed when it is actually selected. And I havent found a good way to do that other than to change the component into a stateful component and use a state var to track which box is ticked.
But when I changed it to a stateful component last night, I really struggled to pass in the list of objects to render.
How can I have a stateful class that also accepts props like a regular functional component?
To begin, you pass props to all types of components in a similar fashion, regardless if it's stateful or not.
<Component prop={someValue}/>
The only difference is how you would access them.
For class-based components you would access them through the props property of the class this.props. i.e.
class Component extends React.Component {
constructor(props) {
// you need to call super(props) otherwise
// this.props will be underfined
super(props);
}
...
someFunction = (...) => {
const value = this.props.prop;
}
}
If you're using TypeScript, you need to describe to it the structure of your props and state like this
interface iComponentProps {
prop: string;
};
interface iComponentState { ... };
export default class Component extends React.Component<iComponentProps, iComponentState> {
...
}
if your component takes in props and/or state and you're unsure of their structure, pass in any for the one you're unsure of.
On the other hand, if I understood your question correctly, you could do something like this:
I also made a demo of the simple app I made to address your other question.
In summary, you can have your AnswerBox component maintain an array of indexes that pertain to each of its choices and have it updated every time a choice is selected (or clicked) by using setState
You can also check out the useState hook to make your functional component stateful.
App.js
import React from "react";
import "./styles.css";
import Question from "./Question";
export default function App() {
const questionData = [
{
question: "Some Question that needs to be answered",
choices: ["Letter A", "Letter B", "Letter C"]
},
{
question: "Another Question that needs to be answered",
choices: ["Letter A", "Letter B", "Letter C"]
}
];
return (
<div className="App">
{questionData.map(question => (
<Question questionText={question.question} choices={question.choices} />
))}
</div>
);
}
Question.js
import React from "react";
import AnswerBox from "./AnswerBox";
const Question = ({ questionText, choices }) => {
return (
<div className={"question-container"}>
<p className={"question-text"}>{questionText}</p>
<AnswerBox choices={choices} />
</div>
);
};
export default Question;
QuestionChoice.js
import React from "react";
import clsx from "clsx";
const QuestionChoice = ({ letter, content, isSelected, handleClick }) => {
return (
<li
className={clsx("question-choice-container", {
"selected-choice": isSelected
})}
>
<input type={"checkbox"} value={content} onClick={handleClick} />
<label for={content} className={"question-choice-label"}>
<strong>{letter.toUpperCase()}. </strong>
<span>{content}</span>
</label>
</li>
);
};
export default QuestionChoice;
AnswerBox.js
import React, { PureComponent } from "react";
import QuestionChoice from "./QuestionChoice";
export default class AnswerBox extends PureComponent {
constructor(props) {
super(props);
this.choiceLetters = Array.from("abcdefghijklmnopqrstuvwxyz");
this.state = { activeChoices: [] };
}
_updateActiveChoices = index => {
let updatedList = [].concat(this.state.activeChoices);
if (this.state.activeChoices.indexOf(index) !== -1) {
updatedList.splice(updatedList.indexOf(index), 1);
} else {
updatedList.push(index);
}
return updatedList;
};
_handleChoiceSelect = choiceIndex => () => {
// an update to your component's state will
// make it re-run its render method
this.setState({ activeChoices: this._updateActiveChoices(choiceIndex) });
};
render() {
return (
<ul class={"answer-box"}>
{this.props.choices.map((choice, index) => (
<QuestionChoice
letter={this.choiceLetters[index]}
content={choice}
isSelected={this.state.activeChoices.indexOf(index) != -1}
handleClick={this._handleChoiceSelect(index)}
/>
))}
</ul>
);
}
}
For this type of interaction you can use the input checkbox's checked attribute to show that its checked. The check state should be derived from somewhere in your state. In the onClick function, you can look at the event for the name and checked state to update your state. Note you could add any attribute you want if name is too generic for you.
The interaction could look like this:
https://codesandbox.io/s/optimistic-archimedes-ozr72?file=/src/App.js
I am attempting to create and render a functional component using the instructions here as a base. From what I've sen in there I should be able to do something along the lines of:
class MyComponent extends React.Component {
render() {
return (
<div>
<OtherComponent props="test" />
</div>
)}
function OtherComponent(props) {
return (
<div>
test
</div>
);
}
}
But this throws the error:
Unexpected token: function OtherComponent(props) {
^
I found a few posts that suggested removing the function so I tried that but then it throws the error:
OtherComponent is not defined
I'm able to get it working by creating a separate class component like so:
class OtherComponent extends React.Component {
render() {
But that's not what I want to do. What is the proper way to create/render a functional component in React.js?
For example this one works. See the docs ;)
React - Composing Components
function OtherComponent(props) {
return <div>test</div>;
}
class App extends React.Component {
render() {
return (
<div>
<OtherComponent props="test" />
</div>
);
}
}
Try this
class MyComponent extends React.Component {
OtherComponent = (props) => {
return (
<div>
test
</div>
);
}
render() {
return (
<div>
{this.OtherComponent("test")}
</div>
)}
}
You can't define a component inside of another component. A functional component means that the component is created from a function and is not a class. It can't have it's own state, because the state is initialized in class constructor. Check out this article for more info https://hackernoon.com/react-stateless-functional-components-nine-wins-you-might-have-overlooked-997b0d933dbc
const otherComponent = (props) =>
<div>
test
</div>;
Here is another way. Its not correct to declare a component in a render function. If it is used solely in a parent component why not make that explicit and use static
class MyComponent extends React.Component {
static myOtherComponent = (props) => <div>{'test'}</div>
render(){
return(
<div>
<MyComponent.myOtherComponent {props} />
</div>
)
}
The myOtherComponent behaviour is controlled purely through the props it gets , it won't have its own state.
Or you could just make it a separate component e.g
export default myOtherComponent = (props) => ()
and import it into MyComponent. Please note , now with hooks ( see React Docs ), you can use hooks to mimic state etc in functional components and the latter approach might be your cleanest and most flexible approach.
This way you can define a function component
function OtherComponent(props) {
return <div>{props}</div>;
}
And now you can use functional component in your App (class component) like below
class App extends React.Component {
render() {
return (
<div>
<OtherComponent props="test" />
</div>
);
}
}
So I am working on a react project, and there is a card that needs to be filled in again and again depending on the use case. Sometimes the card will be filled only one time and sometimes that same card has to be filled in multiple times, how can i get the card component to load multiple times depending on the use case. As soon as someone starts typing things in the first card, I want a new card component to be loaded at the same time just below the already available card component.
I have already created the Card component with input fields in the component, called it IndividualVendor and just one component gets loaded on the initial load. Further such card can be added to add more vendors.
here's the code for the main Beneficiary component and the card container called individualVendor.
import React, { Component } from 'react';
import BeneficiaryFilter from '../../Commons/Filter/Beneficiary/BeneficiaryFilter';
import AddBeneficiary from './AddBeneficiary/AddBeneficiary';
class Beneficiary extends Component {
render() {
return (
<div className="beneficiary-container">
<BeneficiaryFilter />
<div className="main-container">
<AddBeneficiary />
</div>
</div>
);
}
}
export default Beneficiary;
import React, { Component } from 'react';
import IndividualVendor from '../IndividualVendor/InvidualVendor';
class AddBeneficiary extends Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
return (
<div className="add-beneficiary-container">
<IndividualVendor />
</div>
);
}
}
export default AddBeneficiary;
So basically the first component named Beneficiary, will have either one AddBeneficiary component or many of them loaded. I need to know what would be the best way to do this? Hope the question gives you some context. Any help would be very much appreciated, I am very new to programming and trying to learn as much as I can everyday. Please ignore if I have mentioned or asked something too primitive!!
From what I'm understanding, you would like the ability to add a card every time you have a click event in a previous card. You could then create a click function in the parent, pass it down via props to the cards, and let them call it when they're clicked. That could trigger another card to be added.
class Beneficiary extends Component {
this.state = {
children: 1
}
handleClick() {
this.setState({children: this.state.children + 1});
}
render() {
const { children } = this.state;
let cards = [];
for (let i = 0; i < children; i++) {
cards.push(
<AddBeneficiary key={i} handleClick={this.handleClick} />
);
}
return (
<div className="beneficiary-container">
<BeneficiaryFilter />
<div className="main-container">
{ cards }
</div>
</div>
);
}
}
export default Beneficiary;
class AddBeneficiary extends Component {
render() {
const { handleClick } = this.props;
return (
<div
className="add-beneficiary-container"
onClick={handleClick}
>
<IndividualVendor />
</div>
);
}
}
export default AddBeneficiary;
Considering the following three places of defining a functional component in React -
Inside a class (outside the render method)
Inside a class (inside the render method)
Outside the class
In the sample code below, funcComponent1, funcComponent2 and funcComponent3 are defined in the three different locations. How do I consider when to define a functional component in any of these 3 places?
import React, { Component } from 'react';
const FuncComponent1 = (props) => {
return (
<p>{props.name}</p>
)
}
class TestComponent extends Component {
constructor(props){
super(props);
this.state = {
name: "JavaScript"
}
}
FuncComponent2 = (text) => {
return (
<p>{text}, {this.state.name}</p>
)
}
render(){
const FuncComponent3 = (props) => {
return (
<p>{props.text}, {this.state.name}</p>
)
}
return (
<div>
<FuncComponent1 name={'Abrar'} text={'Hello World'}/>
<FuncComponent3 text={"HEllo World"}/>
</div>
)
}
}
export default TestComponent;
You must avoid using functional component inside of render since they will be recreated on every render.
As far as using functions that return JSX inside Class component but outside render` is considered, you can do that when you want to make use of the state or props of the class in order to render JSX content but that which is very specific to the particular class
A functional component outside of React component is most advantageous when the same component can be used at multiple places and hence it makes sense to pass props to it and render it.