I have a list of react-icons passed through props in a "Card Component". In my Card component's render method, I have something like this:
render() {
...
{
this.props.icons.map(icon => {
return (
<div className="icon-square">
/* What should I do here so I render the "icon" variable" */
</div>
)
})
}
}
Note: The list consists of react-icons which are React components themselves.
I tried a lot of things, but I can't quite figure out how I can render the icon. It would be awesome if someone could help me. Thank you
Let say you've passed a list of an icon like
import { FaBeer, FaBug, FaAnchor, FaCoffee } from 'react-icons/fa';
const icons = [FaBeer, FaBug, FaAnchor, FaCoffee];
ReactDOM.render(
<CardComponent icons = {icons} />,
document.querySelector('root')
};
CardComponent.js
class CardComponent extends React.Component{
...
render() {
// Icon is a Component
return (
this.props.icons.map((Icon) => {
return <Icon />
});
)
}
}
If the icon is a react component, then:
this.props.icons.map(Icon => {
return (
<div className="icon-square">
<Icon/>
</div>
)
})
Here is two difference for use your icon, If you pass as a JSX you should use {icon}
But if you pass as a component you should use like this <Icon/>
I think wrapping you need to just put icon is {}
render() {
...
{
this.props.icons.map(icon => {
return (
<div className="icon-square">
{icon}
</div>
)
})
}
}
Related
I am trying to load a component in React via a prop. It is an icon that I want to pass from the parent component.
Dashboard (parent):
import { Button } from './components';
function App() {
return (
<div className="app">
<div className="app__nav">
<Button icon="FiSun" />
<Button icon="FiSun" />
</div>
</div>
);
}
Button (child):
import React from 'react';
import * as Icon from "react-icons/fi";
import './button.scss';
function Button(props) {
return(
<button>
// Something like this
<Icon.props.icon />
</button>
)
}
Unfortunately, I can't find an easy way to make this work since I'm not allowed to use props in the component name.
Here is a working version :
import * as Icons from "react-icons/fi";
function Button(props) {
const Icon = Icons[props.icon];
return <button><Icon/></button>;
}
I added an example on stackblitz
I doubt this is the pattern you want.
If App knows the name of the component Button should render, you really aren't providing any abstraction by not passing the component reference itself. You might be able to get it to work passing the string like this, but I wouldn't recommend going that route.
Instead, I would pass the component reference to Button like this:
import FiSun from '...';
...
<Button icon={FiSun} />
function Button(props) {
const Icon = props.icon; // Alias as uppercase
return(
<button>
<Icon />
</button>
)
}
Or if you want only the Button component to know about the possible icon types, I would suggest using a normal conditional instead of trying to dynamically create the JSX tag:
function Button(props) {
function renderIcon() {
if (props.icon == 'FiSun') {
return <FiSun />;
} // else etc
}
return(
<button>
{renderIcon()}
</button>
)
}
To provide some stability while still keeping the functionality of allowing the component user to pass in any available icon name, you could do something like this:
function Button(props) {
function renderIcon() {
const I = Icon[props.icon];
if (I) {
return <I />;
}
// Icon is not valid, throw error or use fallback.
if (in_development) {
console.error('[Button]: Invalid prop `icon`. Icon '+props.icon+' does not exist.');
}
return <FallbackIcon />
}
return(
<button>
{renderIcon()}
</button>
)
}
i am making a website under react with reactstrap, i have a section that contains charts and a button whose function is to replace said charts with another chart containing more details. however i am struggling to make a concrete code.
i have tried placing the charts in a separate component and have it's content switch through the use of a handleclick function on the button that changes the state of the section (using 'onclick')
i am really not confident in my code's clarity, so i tried reproducing what i did in a simpler matter within fiddle
class hello extends React.Component {
render() {
return (
<h2>hello</h2>
);
}
}
class bye extends React.Component {
render() {
return (
<h2>goodbye</h2>
);
}
}
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<div>
<div>
{this.state.components[hello]}
</div>
<button onClick={this.handleClick}>
switch
{this.setState({components:[<bye />]})}
</button>
</div>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
the div in the "toggle" component is supposed to switch between the components "hello" and "bye"
in effect the current section that is supposed to be displayed ("hello") will be replaced by the other section ("bye") uppon clicking the button under them.
thanks in advance.
If you simply want to toggle between the two components with the button click, you can use conditional rendering.
Change your render method to this:
render(){
return (
<div>
{this.state.isToggleOn?<Hello />:<Bye />}
<button onClick={this.handleClick}>Switch</button>
</div>
}
Also keep your Component's name first character capitalized or react might complain. And using Class based Components is outdated. Hooks are the hot thing right now. So try to use more Functional Components.
Note: My answer assumes you are using babel presets for transpiling jsx and es6 syntax. If not, check out #Colin's answer. It also uses hooks.
why not import all partial views and conditionally render them based on the condition
{condition & <View1/>
There's a few mistakes in your code. Here's an example which does what you want using conditional rendering:
import React, { useState } from "react";
import ReactDOM from "react-dom";
const Hello = () => {
return <h2>hello</h2>;
};
const Bye = () => {
return <h2>bye</h2>;
};
const App = () => {
const [toggled, setToggled] = useState(true);
const handleClick = () => {
setToggled(!toggled);
};
const render = () => {
if (toggled) {
return <Hello />;
}
return <Bye />;
};
return (
<div>
<button onClick={handleClick}>toggle</button>
{render()}
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
There are many ways to do it:
Using conditional operator:
{ this.state.isToggleOn?<Hello/>:<Bye/> }
Using if condition:
render() {
let chart;
if(this.state.isToggleOn) {
chart = <Hello/>;
} else {
chart = <Bye/>;
}
return ( <div> { chart } </div>);
}
3 You can use switch case also for conditional rendering. Here it is not well suited as condition is true or false.
I have 2 component a parent component to manage the state and a lot of other things and a child component with some reactstrap buttons radio i'm trying to change the state onClick on the child buttons but I get the error: this.setState is not a function and i can't figure out what's wrong with my code =>
//Parent
import React, { Component } from 'react';
import BtnRadio from './btnToggle';
class parent extends Component {
state = {
rSelected: true,
}
onRadioBtnClick(rSelected) {
this.setState({
rSelected:rSelected
});
}
render(){
return (
<div>
<BtnToggle onRadioBtnClick={this.onRadioBtnClick} active={this.state.rSelected}/>
</div>
);
}
};
export default AddAdmin;
//Chlid
import React from 'react';
import { Button, ButtonGroup } from 'reactstrap';
const BtnRadio = (props) => {
return (
<ButtonGroup>
<Button color="light" onClick={() => props.onRadioBtnClick(true)} active={props.active === true}>Enable</Button>
<Button color="light" onClick={() => props.onRadioBtnClick(false)} active={props.active === false}>Disabled</Button>
</ButtonGroup>
);
};
export default BtnRadio;
is there someone who can point me to the right direction i guess that i forgot to bind something...
The problem is, when you're using non-anonymous functions, this gets overridden, and wont refer to the component anymore. Since you're already using class properties, the simple fix, is to keep using the arrow functions, to keep this referencing the component:
onRadioBtnClick = (rSelected) => {
this.setState({
rSelected:rSelected
});
}
See #5 in this medium article, which explains different ways of binding this to keep it referencing the component.
<BtnToggle onRadioBtnClick={() => this.onRadioBtnClick()} active={this.state.rSelected}/>
Arrow function for the rescue.
You should bind the functions your passing like so:
class parent extends Component {
state = {
rSelected: true,
}
onRadioBtnClick(rSelected) {
this.setState({
rSelected:rSelected
});
}
render(){
return (
<div>
<BtnToggle onRadioBtnClick={this.onRadioBtnClick.bind(this)} active={this.state.rSelected}/>
</div>
);
}
}
alternatively, you can bind the functions before passing them in the constructor:
class parent extends Component {
state = {
rSelected: true,
}
constructor() {
super()
this.onRadioBtnClick = this.onRadioBtnClick.bind(this)
}
onRadioBtnClick(rSelected) {
this.setState({
rSelected:rSelected
});
}
render(){
return (
<div>
<BtnToggle onRadioBtnClick={this.onRadioBtnClick} active={this.state.rSelected}/>
</div>
);
}
}
I am building an App where Serverinput is being rendered.
Now I am trying to figure out, how it´s possible to display pure HTML. Because now it is only displayed as ...., where it should show an Image.
The question is, where should one call dangerouslySetInnerHTML() here to display all HTML as requested?
The Strings are being stores in an Array(messageList) that is being mapped. Userinput is escaped, so theres no problem on that side.
let listItems = messageList.map(d => (
<div >
<p className={d.senderId + "timestamp"}>{d.time}</p>
<p className={d.senderId} key={d.idCount} ref={d.idCount}>
{" "}
{d.text}{" "}
</p>
</div>
));
let gif1 = <img className="gif" alt="" src={gif} />;
return (
<div >
<div dangerouslySetInnerHTML={__html: {listItems}} />
<ul>{listItems}</ul>
</div>
);
Thanks a lot for any help that is given.
I updated the dangerousHTML where I thought it would work. But now it throws - Syntax error: Unexpected token, expected }
You shuould have something like this for each element that you want to show the dynamic content
<div dangerouslySetInnerHTML={__html: {yourHtmlContent}} />
How I understood is that Array(messageList) contains a list of markup strings.
So you just need to join them.
const messageList = [
"<h2>Header</h2>",
"<body>This is body!</body>",
"<footer>Footer!</footer>"
];
function createMarkup() {
return { __html: messageList.join("") };
}
function MyComponent() {
return <div dangerouslySetInnerHTML={createMarkup()} />;
}
It'd display something like this
You need to pass a string not an object as you do here.
(as it's just an implementation of innerHTML)
{__html: {listItems}}
Full source for completeness.
import React, { Component } from "react";
import { render } from "react-dom";
import "./styles.css";
const messageList = [
"<h2>Header</h2>",
"<body>This is body!</body>",
"<footer>Footer!</footer>"
];
function createMarkup() {
return { __html: messageList.join("") };
}
function MyComponent() {
return <div dangerouslySetInnerHTML={createMarkup()} />;
}
class App extends Component {
constructor() {
super();
this.state = {
name: "React"
};
}
render() {
return (
<div>
<MyComponent />
</div>
);
}
}
render(<App />, document.getElementById("root"));
Working demo.
I'm having a problem with my React component. The nested children of my component ControlPanel don't seem to be rendering. Here is my code:
class App extends Component {
render() {
return (
<div className="App">
<ControlPanel>
<CustomerDisplay />
</ControlPanel>
</div>
);
}
}
I have the following two lines at the top of this file:
import ControlPanel from './components/control_panel';
import CustomerDisplay from './components/customer_display';
And here is my ControlPanel Component:
import React from 'react';
import CSSModules from 'react-css-modules';
import styles from './styles.scss';
const ControlPanel = () => {
return (
<div className="control_panel" id="control_panel">
</div>
);
}
export default CSSModules(ControlPanel, styles);
I have tried:
Calling the component as a full HTML tag (opening & closing)
Nesting the CustomerDisplay component in the ControlPanel component (in the ControlPanel's index.jsx file)
I know that nesting component's is possible. I've seen it done. For some reason it just won't work for me.
To allow components to contain children and render them correctly, you have to use this.props.children. This is passed to all components with children as a prop and contains the children of the component, as explained by the React documentation:
Containment
Some components don't know their children ahead of time. This is especially common for components like Sidebar or Dialog that represent generic "boxes".
We recommend that such components use the special children prop to pass children elements directly into their output:
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
This lets other components pass arbitrary children to them by nesting the JSX
function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}
As described in the documentation, some components don't know their children ahead of time -- they may be generic wrappers or boxes of content that vary, which is what your ControlPanel is. So, to render the children of your component, you must render the children from the children prop explicitly in the parent's render method. Thus, apply it like this to your code:
const ControlPanel = (props) => {
return (
<div className="control_panel" id="control_panel">
{props.children}
</div>
);
}
Notice how props.children is rendered (not this.props.children because it is a function component).
You can access the nested elements through props. So in your case do this:
const ControlPanel = (props) => {
return (
<div className="control_panel" id="control_panel">
{props.children}
</div>
);
}
You need to render the children in ControlPanel
const ControlPanel = ({ children }) => {
return (
<div className="control_panel" id="control_panel">
{children}
</div>
);
}
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
export default function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}
Anything inside the<FancyBorder>JSX tag gets passed into the FancyBorder component as childrenprop. Since FancyBorder renders {props.children} inside a <div>, the passed elements appear in the final output.
This is what I was looking after, check it out here
https://reactjs.org/docs/composition-vs-inheritance.html
Your App.js (I understand that it is your JSX Index) should be:
import React from 'react';
import ReactDOM from 'react-dom';
export default class App extends Component {
render() {
return (
<div className="App">
<ControlPanel>
<CustomerDisplay />
</ControlPanel>
</div>
);
}
}
ReactDOM.render(<App/>, document.getElementById('YOUR_ROOT_ID'));
Try to add export default before class (in all your components).