I have this code...
import React from 'react';
import Fade from 'react-reveal/Fade';
import { Novice } from '../../../Icons/Novice';
import { Apprentice } from '../../../Icons/Apprentice';
import { Explorer } from '../../../Icons/Explorer';
import { Advocate } from '../../../Icons/Advocate';
import { Profesional } from '../../../Icons/Profesional';
import { Ambassador } from '../../../Icons/Ambassador';
export const ProfileType = (props) => {
const {
label = 'Apprentice',
} = props;
return (
<Fade top delay={2100}>
<div className="profile-card__work-type">
{
/* {label === 'Novice' && <Novice />}
{label === 'Apprentice' && <Apprentice />}
{label === 'Explorer' && <Explorer />}
{label === 'Advocate' && <Advocate />}
{label === 'Profesional' && <Profesional />}
{label === 'Ambassador' && <Ambassador />} */
}
{ `< ${label} />` }
<span className="profile-card__work-type-text">
{' '}
{label}
</span>
</div>
</Fade>
);
};
I want to use the 'label' variable as the name of my component and I'm trying to use string interpolation to use it like this { `< ${label} />` } but it just prints out the string < Novice/> to screen.
How can use the label varible as the name of the component instead of the several lines of conditionals?
Best wishes,
Nicolas
Use an object to store your components...
const obj = {
Novice: <Novice />,
Apprentice: <Apprentice />,
Explorer: <Explorer />,
Advocate: <Advocate />,
Profesional: <Profesional />,
Ambassador: <Ambassador />
};
And then access the object using the label.
{obj[label]}
You can't just put < ${label} /> and expect it to be evaluated as a Component if your really want to do this you have to use the dangerouslySetInnerHTML prop on a <div/> or something and give it a valid html String. But as the name of the prop suggests its dangerous given the possibility of remote code execution attacks.
Another way of going about this and probably the best way, like the other Answer suggests is putting your Components already in an Array or as properties of an Object and accessing them through the index of the Component in the Array or the key of the Object property.
Related
I'm trying to get rid of a warning message in the project I'm working on.
index.js:1 Warning: findDOMNode is deprecated in StrictMode. findDOMNode was passed an instance of Transition which is inside StrictMode. Instead, add a ref directly to the element you want to reference. Learn more about using refs safely here: https://reactjs.org/link/strict-mode-find-node
at div
at Transition (http://localhost:3000/static/js/vendors~main.chunk.js:47483:30)
at CSSTransition (http://localhost:3000/static/js/vendors~main.chunk.js:46600:35)
at div
at TransitionGroup (http://localhost:3000/static/js/vendors~main.chunk.js:48052:30)
at Contacts (http://localhost:3000/static/js/main.chunk.js:1623:96)
at div
at div
at Home (http://localhost:3000/static/js/main.chunk.js:2549:88)
at AuthCheck (http://localhost:3000/static/js/main.chunk.js:2705:5)
at Routes (http://localhost:3000/static/js/vendors~main.chunk.js:45749:5)
at div
at Router (http://localhost:3000/static/js/vendors~main.chunk.js:45682:15)
at BrowserRouter (http://localhost:3000/static/js/vendors~main.chunk.js:45198:5)
at ContactState (http://localhost:3000/static/js/main.chunk.js:3743:85)
at AuthState (http://localhost:3000/static/js/main.chunk.js:3243:85)
at AlertState (http://localhost:3000/static/js/main.chunk.js:2844:85)
at App
The problematic code:
import React, { Fragment, useEffect } from 'react';
import { CSSTransition, TransitionGroup } from 'react-transition-group';
import { useContactContext } from '../../context/contact/contactContext';
import { useAuthtContext } from '../../context/auth/authContext';
import ContactItem from './ContactItem';
import Spinner from '../layout/Spinner';
const Contacts = () => {
const { contacts, filtered, getContacts, loading } = useContactContext();
const { isAuthenticated } = useAuthtContext();
useEffect(() => {
if (isAuthenticated) {
getContacts();
}
// eslint-disable-next-line
}, [isAuthenticated]);
if (!loading && contacts !== null && contacts.length === 0) {
return <h4>Please add a contact</h4>;
}
return (
<Fragment>
{contacts === null || loading ? (
<Spinner />
) : (
<TransitionGroup>
{(filtered || contacts).map((contact) => (
<CSSTransition timeout={1000} classNames="item" key={contact._id}>
<ContactItem contact={contact} />
</CSSTransition>
))}
</TransitionGroup>
)}
</Fragment>
);
};
export default Contacts;
I've spent a few hours looking for answers, but I feel like I'm running around in an endless loop.
To get rid of the warning, I need to use useRef hooks on each CSSTransition element, to connect it with (it's children?).
I can't use useRef() inside the render function of a component, so I defined a new component to display each TransitionItem:
...
const TransitionItem = ({ contact, ...props }) => {
const ref = useRef(null); // Had to use this ref to go around a warning
return (
<CSSTransition nodeRef={ref} timeout={1000} classNames="item" {...props}>
<div ref={ref}>
<ContactItem contact={contact} />
</div>
</CSSTransition>
);
};
return (
<Fragment>
{contacts === null || loading ? (
<Spinner />
) : (
<TransitionGroup>
{(filtered || contacts).map((contact) => (
<TransitionItem key={contact._id} contact={contact} />
))}
</TransitionGroup>
)}
</Fragment>
);
...
Now every time I try to click on a button, to remove an item from the list, I see a "flashing" effect, you can check out in this Sandbox: (Click on the red buttons to remove an item)
https://codesandbox.io/s/kind-feather-2psuz
The "flashing" problem only starts when I move the CSSTransition component into the new TransitionItem component, but I can't use useRef hooks on each item if I don't move it there.
Help pls! :)
PS:
Removing <React.StrictMode> from the index.js is not a solution to the root problem.
I have the same warning in my project and i can fix it with this solution, thank pixel-fixer !
Issue #668 on repo react-transition-group
From 4.4.0 release notes:
react-transition-group internally uses findDOMNode, which is
deprecated and produces warnings in Strict Mode, so now you can
optionally pass nodeRef to Transition and CSSTransition, it's a ref
object that should point to the transitioning child:
You can fix this like this
import React from "react"
import { CSSTransition } from "react-transition-group"
const MyComponent = () => {
const nodeRef = React.useRef(null)
return (
<CSSTransition nodeRef={nodeRef} in timeout={200} classNames="fade">
<div ref={nodeRef}>Fade</div>
</CSSTransition>
)
}
I hope it works for you, have a nice day !
There are similar questions posed here and on Google, but none with answers that fit my scenario.
Basically, I want to display a different searchbar in the header, depending on what page I am on. This is nextjs.
But when reloading the page, I get the error in console:
Warning: Expected server HTML to contain a matching <div> in <div>
First thing I tried was
const currentPath = Router.router?.route;
return (
<div className="sub-bar">
{currentPath === '/products' && (
<Search />
)}
{currentPath === '/baseballcards' && (
<SearchBaseballCards />
)}
</div>
);
That generates the error when reloading the page, even if I comment either of them out.
Next thing I tried was the ternary route:
<div className="sub-bar">
{currentPath === '/baseballcards' ? <SearchBaseballCards /> : <Search />}
</div>
This actually worked but ternary is no good because I only want the search bar on the /products and /baseballcards pages.
Final thing I tried was:
const currentPath = Router.router?.route;
let searchbar;
if (currentPath === '/baseballcards') {
searchbar = <SearchBaseballCards />
}
else if (currentPath === '/products') {
searchbar = <Search />
}
else {
searchbar = null;
}
return (
<div className="sub-bar">
{searchbar}
</div>
);
This gives me the original error when reloading the page, so back to square one.
https://reactjs.org/docs/conditional-rendering.html
Rather than accessing the Router object directly with Router.router?.route, use the router instance returned by the useRouter hook. This will ensure that the rendering between server and client is consistent.
import { useRouter } from 'next/router';
const SearchBar = () => {
const router = useRouter();
const currentPath = router.asPath;
return (
<div className="sub-bar">
{currentPath === '/products' && (
<Search />
)}
{currentPath === '/baseballcards' && (
<SearchBaseballCards />
)}
</div>
);
};
I have a demo available on StackBlitz. This is very pseudo code and my actual code is a lot more complicated.
I have three components loaded into index.tsx. Each component has a conditional that checks the boolean above. My actual code checks data from a GraphQL call.
If the component is shown I want to store the text. I'm trying to do this with useState. So, if the About section is shown add 'About' to the nav state.
I'm getting the error:
Operator '<' cannot be applied to types 'void' and 'number'
Can anyone explain this error, is it possible to do this?
import React, { useState } from "react";
import { render } from "react-dom";
import About from "./About";
import Products from "./Products";
import Contact from "./Contact";
import "./style.css";
const App = () => {
const [nav, setNav] = useState([])
const about = true;
const products = true;
const contact = true;
return (
<div>
{about && (
setNav([...nav, 'About'])
<About />
)}
{products && (
setNav([...nav, 'Products'])
<Products />
)}
{contact && (
setNav([...nav, 'Contact'])
<Contact />)}
</div>
);
};
render(<App />, document.getElementById("root"));
I'm not sure if I understand your question correctly. If your code checks data from a GraphQL call, you should update the nav state from there (right after you received the response) and display the components based on that state, not the other way around.
You can use the useEffect hook to grab the response and update your state. I would also use an object instead of an array to store the nav state (it's easier to update its values).
function App() {
const [nav, setNav] = React.useState({
about: false,
contact: false,
products: false,
});
React.useEffect(() => {
// make the graphQL call here and based on the response update the state:
const graphqlResponse = {
about: true,
contact: false,
products: true,
// ... others
}
setNav({
about: graphqlResponse.about,
contact: graphqlResponse.contact,
products: graphqlResponse.products,
});
}, []);
return (
<div>
{nav.about && <About />}
{nav.products && <Products />}
{nav.contact && <Contact />}
<pre>{JSON.stringify({nav}, null, 2)}</pre>
</div>
);
};
function About() { return <h2>About</h2> }
function Contact() { return <h2>Contact</h2> }
function Products() { return <h2>Products</h2> }
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
This should work:
{ about && setNav(["About", ...nav]) && <About /> }
{ products && setNav(["Product", ...nav]) && <Products /> }
{ contact && setNav(["Contact", ...nav]) && <Contact /> }
So I'm making a todo list app and to provide alternate colors to new todo items as they are added in the todolist i have used the if else statement in React component but i think it is not evaluating.
Below are the two classes from my css file that i'm using -->
.bgColorItem1{
background-color: white;
}
.bgColorItem2{
background-color:grey;
}
Below is the component that accepts arguments item(todo item that will be added to the list) and key(index passed as key) from todolist -->
import React from 'react';
import './App.css';
const TodoItem=({item,key})=>{
let settingClass="";
// *****so the problem is here in the if condn that's not putting settingClass as bgColorItem1*****
if(key%2===0){
settingClass="bgColorItem1";
}else{
settingClass="bgColorItem2";
}
return <div className={`boxSpace centerAligning ${settingClass}`}>{item}</div>
}
export default TodoItem;
So what i expect from this code that key which was index in todolist component passed to todo here above should return 0 for even so that settingClass can have alternate values and hence provide alternate colors.But that is not the case.
First of all, don't use key's value since it is internal. Secondly, you can achieve like this
{items.map((item, index) => <TodoItem item={item} index={index} />)}
In TodoItem
const TodoItem=({ item, index })=>{
let settingClass=index % 2 === 0 ? 'bgColorItem1' : 'bgColorItem2';
return <div className={`boxSpace centerAligning ${settingClass}`}>{item}</div>
}
However, you don't need react to do this, just use css, in your css
.boxSpace:nth-child(odd) {
background: red;
}
.boxSpace:nth-child(even) {
background: blue;
}
You can't use key property as it is reserved, also you will get a warning for it:
Warning: ListItem: key is not a prop. Trying to access it will result in undefined being returned. If you need to access the same value within the child component, you should pass it as a different prop.
Simple example:
const ListItem = ({ key }) => {
console.log(key);
return <div>{key}</div>;
};
const App = () => {
return (
<>
{[0, 1, 2, 4].map(item => (
<ListItem key={item} />
))}
</>
);
};
Your TodoItem component shouldn't have to care about the key prop, but rather a boolean (or a string if you have more than 2 styles) prop like alternateStyle:
const TodoItem=({item,alternateStyle})=>{
return <div className={
`boxSpace centerAligning ${alternateStyle ? 'bgColorItem2' :
'bgColorItem1'}`
}>
{item}
</div>
Then you can set the value you need to alternateStyle in the parent component:
<div>
{items.map((item, index) => <TodoItem item={item} alternateStyle={index % 2 !== 0} />)}
</div>
key "prop" is reserved in React. You cannot use it. Rename this props to "idx"
See: Special props
I know there are many answers out there for this issue but I couldn't find one that exactly solved my problem. I am getting the following error : Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of QuestionItem. See https://fb.me/react-warning-keys for more information.
I am setting a key for the component but I can't get the warning to go away.
Main component :
renderData() {
return this.state.data.map((data) => {
return (
<QuestionItem key={data._id} data={data} delete={this.deleteItem} edit={this.editItem} />
)
})
}
QuestionItem component :
import React, { Component, PropTypes } from 'react';
import Card from 'material-ui/lib/card/card';
import CardActions from 'material-ui/lib/card/card-actions';
import CardHeader from 'material-ui/lib/card/card-header';
import CardMedia from 'material-ui/lib/card/card-media';
import CardTitle from 'material-ui/lib/card/card-title';
import FlatButton from 'material-ui/lib/flat-button';
import CardText from 'material-ui/lib/card/card-text';
import Delete from 'material-ui/lib/svg-icons/action/delete';
import ModeEdit from 'material-ui/lib/svg-icons/editor/mode-edit';
import IconButton from 'material-ui/lib/icon-button';
import Button from '../UI/Button';
class QuestionItem extends Component {
renderTags() {
return this.props.data.tag.map((tag) => {
return (
<FlatButton label={tag} />
)
})
}
renderCompany() {
return this.props.data.company.map((company) => {
return (
<FlatButton label={company} />
)
})
}
edit = () => {
this.props.edit(this.props.data);
}
delete = () => {
this.props.delete(this.props.data._id);
console.log(this.props.data._id);
}
render() {
return (
<Card style={{margin: 50}}>
<CardTitle title={this.props.data.text} />
<CardText>
{this.props.data.answer}
</CardText>
<CardActions>
{ this.renderTags() }
{ this.renderCompany() }
<IconButton onClick={this.delete} style={{float: 'right'}}>
<Delete />
</IconButton>
<IconButton onClick={this.edit} style={{float: 'right'}}>
<ModeEdit />
</IconButton>
</CardActions>
</Card>
)
}
}
export default QuestionItem;
What am I missing here?
Well you'll need to log out the data._id and verify that they are all unique. Or you can do this:
renderData() {
return this.state.data.map((data, index) => {
return (
<QuestionItem key={index} data={data} delete={this.deleteItem} edit-{this.editItem} />
);
});
}
As the other answer pointed out, the other calls to map that go to a render need to set the key prop too to a unique value.
So these:
renderTags() {
return this.props.data.tag.map((tag) => {
return (
<FlatButton label={tag} />
)
})
}
renderCompany() {
return this.props.data.company.map((company) => {
return (
<FlatButton label={company} />
)
})
}
Should become:
renderTags() {
return this.props.data.tag.map((tag, index) => {
return (
<FlatButton key={index} label={tag} />
);
});
}
renderCompany() {
return this.props.data.company.map((company, index) => {
return (
<FlatButton key={index} label={company} />
);
});
}
Note we are using index which is the array index. It is basically like a synthetic identifier in SQL. If what you're actually rendering has unique identifiers already, it is better to use those! For example, the key prop for a tag could be just the tag -- the string itself. The key prop supports multiple types:
react - nodes-and-elements:
key : string | boolean | number | null,
So if your tags are unique (I would expect them to be but obviously don't want to assume), you could do this:
renderTags() {
return this.props.data.tag.map((tag) => {
return (
<FlatButton key={tag} label={tag} />
);
});
}
You might consider doing instead something like (tag || '').toLowerCase().replace(' ', '_') however I think React is already doing some manipulation there (besides potentially character case). So just passing the tag itself should be good! You can inspect the DOM to see data-reactid if you're not running a version that got rid of it (I think 0.15 gets rid of it). The React developer tools might let you inspect the key with 0.15.
Update
I do not recommend using the array index as the key. It causes subtle bugs. To see this in action, make an array of objects, render them using the array index and then mutate the array by removing say the 2nd element (and ensure React renders again). Now the indexes don't correspond to the same objects. My recommendation is to always set a key to a unique value. During development, it might be best not to set a key until you find one rather than using the array index because then the errors on the console will remind you to fix this before deploying/committing your change.
In renderTags() and renderCompany() you have iterators with no keys.