Rendering a list into ListItems and Links - javascript

class Glass extends React.Component {
constructor(props) {
super(props);
this.state = {
names: [{n: "Gemma", num: "01"}, {n: "Katie", num: "02"}],
}
}
render() {
const { list } = this.state;
return (
<div>
<List component="hello">
{list.map(name => {
return (
<ListItem key={name.num}>
<Link to="/glassmates/" + name.num > //I keep getting error here saying unexpected token.
{name.n}
</Link>
</ListItem>
<Divider /> // also getting errror here saying adjacent jsx elements must be wrapped in an enclosing tag
)
})}
</List>
</div>
);
}
}
I listed some errors I'm getting in the code, but I'm confused in general whether this is how you render a list into Links inside a ListItem and a List component...

{list.map(name => {
return (
<div>
<ListItem key={name.num}>
<Link to="/glassmates/" + name.num > //Because your state not contains list==>names
{name.n}
</Link>
</ListItem>
<Divider /> // JSX should have on parent
</div>
)
})}

Concerning you errors: for the first error, you should be using string interpolation, i.e:
<Link to=`/glassmates/${name.num}` >
Secondly, as stated adjacent jsx elements must be wrapped in an enclosing tag.
Which means you could/should wrap your elements in a div:
<div> // parent
...
<Divider />
</div>

Related

How to make React page render only change component instead of the whole page?

So I have a toggle looking like this (see below), but the page always re-render the whole thing on the first time I click on the toggle.
export default function Toggle({isChecked, label}: Props) {
return (
<Wrapper>
<Switch isChecked={isChecked}>
<span />
</Switch>
{label}
</Wrapper>
)
}
Then another component which is using this Toggle component
export default function ToggleBox({isChecked, label, children}: Props) {
return (
<Wrapper>
<Toggle isChecked={isChecked} label={label} />
<Content>{children}</Content>
</Wrapper>
)
}
There is a layout
export default function Layout({...someprops bla bla, children}: Props) {
<Wrapper>
<DesktopImg>
<ImageWrapper>
<Image src={image.url} alt={`${headline} image`} layout="fill" />
</ImageWrapper>
</DesktopImg>
<div>
<Content>
{back && backButton}
<MobileImg>
<Image
src={image.url}
alt={`${headline} image`}
width={image.width}
height={image.height}
/>
</MobileImg>
{headline}
<P gutterSize="medium">{description}</P>
</Content>
<ChildrenContainer>{children}</ChildrenContainer>
</div>
</Wrapper>
}
Then finally the page which use the ToggleBox.
export default function Index({isChecked, label, children}: Props) {
const [check, setCheck] = useState(false)
return (
<Layout>
<div onClick={() => setCheck(!check)}>
<ToggleBox label="some label..." isChecked={check}>
//sometext..
</ToggleBox>
</div>
<Button onClick={nextPage} disabled={!check}>
Next
</Button>
</Layout>
)
}
I kinda tried to use the React.memo method but it doesnt seem to work. Any suggestion to make the page not re-render the whole thing but just the toggle?
Move your state further down the tree, you want it to be as close to the component(s) it impacts as possible, again this will probably require breaking out into smaller components, for example, break out the following into a seperate component -
const NewToggleComponent = () => {
const [check, setCheck] = useState(false)
return (
<div onClick={() => setCheck(!check)}>
<ToggleBox label="some label..." isChecked={check}>
//sometext..
</ToggleBox>
</div>
<Button onClick={nextPage} disabled={!check}>
Next
</Button>
)
}
remove state from the top level component, and use this component in your top level component -
...
<NewToggleComponent />
...

React - Each child in array .. unique "key" prop warning

I appear to have a decent understanding of this principal, which allows me to get by, until now. I am applying a key prop to all children of all iterators, and yet I'm still getting this warning.
A FacilitiesContainer is rendering a FacilitiesComponent, which in turn renders a list of Facilities, which renders a list of Courses. A Course does not use an iterator. However, the FacilitiesContainer is passing the FacilitiesComponent through a HOC, which is returning the final component. There's nothing in the HOC that modifies the passed components, so I'm not sure if this is a problem.
// The render method of FacilitiesContainer
render = () => {
let FacilitiesWithSearch = SearchHOC(
BasicSearch,
FacilitiesComponent,
{data: this.state.facilities }
);
return <FacilitiesWithSearch />;
}
class FacilitiesComponent extends Component {
renderFacilities = () => (
this.props.data.map((facilityData, index) =>
<Facility key={index} data={facilityData} />
)
)
render = () => (
<Grid>
<Row>
<Col xs={12} sm={8} smOffset={2} md={8} mdOffset={1}>
{
this.props.data.length > 0
? this.renderFacilities()
: <div>No results</div>
}
</Col>
</Row>
</Grid>
)
}
const Facility = ({ data }) => (
<Panel>
<Panel.Heading>
<Panel.Title>{data.Name}</Panel.Title>
</Panel.Heading>
<Panel.Body>
<Grid>
<Row>
<p><b>Address:</b><br />
{data.Street}<br />
{data.City}, {data.State} {data.Zip}
</p>
<p><b>Phone:</b> {data.Phone}</p>
{
data.Courses.map((courseData, index) =>
<p><Course key={index} data={courseData} /></p>)
}
</Row>
</Grid>
</Panel.Body>
</Panel>
);
You indeed didn't provide keys to p elements here:
{
data.Courses.map((courseData, index) =>
<p><Course key={index} data={courseData} /></p>)
}
Should be
{
data.Courses.map((courseData, index) =>
<p key={index}><Course data={courseData} /></p>)
}
Try to append a string to the index before assigning it to the key. That's because you are only using index (0,1,2...) both for your list of facilities and list of courses, so there will be duplicated indexes in the final rendered component. If you do as below you ensure that each index is unique:
<Facility key={`facility_${index}`} data={facilityData} />
and
<Course key={`course_${index}`} data={courseData} />

React JS - Nesting components passed as props

I've written a container component in ReactJS and am passing in a prop to that component which is to be rendered as the 'main' content, like so:
class RegistrationContainer extends Component {
render() {
const MainContent = this.props.mainContent;
return (
<Row>
<Col offset="lg-3" lg={6}>
<MainContent />
</Col>
</Row>
);
}
}
export default RegistrationContainer;
And I'm passsing to it a mainContent prop like so:
import RegistrationContainer from './RegistrationContainer';
import RegistrationEntryView from './RegistrationEntryView';
class RegistrationCodeEntry extends Component {
render() {
return (
<RegistrationContainer mainContent={RegistrationEntryView} />
);
}
}
export default RegistrationCodeEntry;
My issue is that I would like RegistrationEntryView to have props, but can't seem to figure out how to define/pass in props on it. If I do the following I get an error:
class RegistrationCodeEntry extends Component {
render() {
const RegistrationView = <RegistrationEntryView someProp="blah" /> ;
return (
<RegistrationContainer mainContent={RegistrationView} />
);
}
}
export default RegistrationCodeEntry;
Error is as follows:
invariant.js?7313:42 Uncaught Error: Element type is invalid: expected
a string (for built-in components) or a class/function (for composite
components) but got: object. Check the render method of
RegistrationContainer.
Is this something that this.props.children could solve? I've been struggling to get my head around the concept of that, so any advice on where I'm going wrong would be appreciated.
You can solve this with this.props.children like this
class RegistrationCodeEntry extends Component {
render() {
return (
<RegistrationContainer>
// Render it as children
<RegistrationEntryView someProp="blah" />
</RegistrationContainer>
);
}
}
then in your container
class RegistrationContainer extends Component {
render() {
const MainContent = this.props.mainContent;
return (
<Row>
<Col offset="lg-3" lg={6}>
// Render the passed children
{this.props.children}
</Col>
</Row>
);
}
}
Your approach is correct. You just went wrong here:
<Row>
<Col offset="lg-3" lg={6}>
<MainContent />
</Col>
</Row>
Instead do this:
<Row>
<Col offset="lg-3" lg={6}>
{ MainContent }
</Col>
</Row>
I personally think, this approach is better than using children.
When you did this - const RegistrationView = <RegistrationEntryView someProp="blah" /> ; The component was already rendered and converted to appropriate format. Hence you cannot re-render it with <MainContent />.
So using {} is correct in this case.
Good Luck!

Material-UI BottomNavigationItem URL

UI on a react component. I have a <BottomNavigationItem /> component. This actually renders as an <button>. How can I actually make it render/navigate to a URL?
class FooterNavigation extends Component {
state = {
selectedIndex: 0,
};
select = (index) => this.setState({selectedIndex: index});
render() {
return (
<footer className="mdl-mini-footer">
<Paper zDepth={1}>
<BottomNavigation selectedIndex={this.state.selectedIndex}>
<BottomNavigationItem
label="Reviews"
icon={reviewIcon}
onClick={() => this.select(0)}
/>
</BottomNavigation>
</Paper>
</footer>
);
}
}
Simply you can just add containerElement={<Link to="/home"/>} don't forget to import Link from react-router-dom
So it will be like this:
<BottomNavigationItem
containerElement={<Link to="/home"/>}
label="Reviews"
icon={reviewIcon}
onClick={() => this.select(0)}
/>

Check if react element is empty

I don't want to render the title when description is empty
var description = <MyElement />; // render will return nothing in render in some cases
if (!description) { // this will not work because its an object (reactelement)
return null;
}
<div>
{title}
{description}
</div>
Whats the correct way instead of !description to check if its empty?
var description, title;
if (this.props.description) {
description = <Description description={this.props.description} />;
if (this.props.title) {
title = <Title title={this.props.title} />;
}
}
<div>
{title}
{description}
</div>
Or:
render() {
const { description, title } = this.props;
return (
<div>
{title && description && <Title title={title} />}
{description && <Description description={description} />}
</div>
);
}
Imo it's better practice that if your description element isn't needed then it isn't rendered, rather than returning null in it's render. Since you would likely be sending the data through a prop. And likewise if you don't want to render this component at all, then that should happen in the parent.

Categories