Problems with unique key prop with "useBreadcrumbs from use-react-router-breadcrumbs" - javascript

I'm having problems understanding how to apply key attribute for my breadcrumbs router component. I am getting this error in the console.
Warning: Each child in a list should have a unique "key" prop.
Check the render method of `BreadCrumbsRouterComponent`. See https://reactjs.org/link/warning-keys for more information.
at BreadCrumbsRouterComponent (http://localhost:3000/static/js/bundle.js:710:95)
at span
at div
at BreadCrumbsSection
at CategoriesView
at RenderedRoute (http://localhost:3000/static/js/bundle.js:42813:5)
at Routes (http://localhost:3000/static/js/bundle.js:43235:5)
at Router (http://localhost:3000/static/js/bundle.js:43173:15)
at BrowserRouter (http://localhost:3000/static/js/bundle.js:41505:5)
at App (http://localhost:3000/static/js/bundle.js:463:83)
This is my breadcrumbs component. I used documentation from these pages:
https://www.npmjs.com/package/use-react-router-breadcrumbs
https://stackblitz.com/edit/github-fiw8uj?file=src%2FApp.tsx
import React from 'react'
import { NavLink } from 'react-router-dom';
import useBreadcrumbs from "use-react-router-breadcrumbs";
const BreadCrumbsRouterComponent = () => {
const breadcrumbs = useBreadcrumbs();
return (
<>
{breadcrumbs.map(({ breadcrumb, match }, index) => (
<>
<NavLink key={match.pathname} to={match.pathname}>
{breadcrumb}
</NavLink>
{index !== breadcrumbs.length - 1 && '\u00a0\u00a0>\u00a0\u00a0'}
</>
))}
</>
)
}
export default BreadCrumbsRouterComponent
This is my breadcrumbs section of which i use in my views.
import React from 'react'
import BreadCrumbsRouterComponent from '../components/BreadCrumbsRouterComponent'
const BreadCrumbsSection = () => {
return (
<div className="breadcrumb-container">
<span className="container">
<BreadCrumbsRouterComponent />
</span>
</div>
)
}
export default BreadCrumbsSection
This is my CSS
.breadcrumb-container {
.container{
display: flex;
margin-top: 1rem;
#include font-16-medium;
#include xxl-min{
max-width: 1110px;
}
.active{
color: $color-grey;
&:last-child{
color: $color-theme;
}
}
}
}
I've tried moving the "key" higher in the hierarchy by adding a new element. It solves the error but then i lose the control in CSS of which i want to use :last-child selector to highlight the current page.

I'm not sure, but you might need to put key into fragment component.
{breadcrumbs.map(({ breadcrumb, match }, index) => (
<React.Fragment key={match.pathname}> // here
<NavLink to={match.pathname}>
{breadcrumb}
</NavLink>
{index !== breadcrumbs.length - 1 && '\u00a0\u00a0>\u00a0\u00a0'}
<React.Fragment />
))}

Related

Conditional link styling React

I want my nav bar to style the page title I'm in, im using React and Tailwind CSS, for example, just make the title yellow when im on the selected path.
My logic to achieve that would be this but isn't working:
<div className={active ? "text-yellow-400" : undefined}
My rout code:
const LinkItem = ({href, path, children, ...props}) => {
const active = path === href
return (
<NextLink href={href}>
<div className={active ? "text-yellow-400" : undefined}
{...props}
>
{children}
</div>
</NextLink>
)
}
Nav bar code:
const Navbar = props => {
const {path} = props
return (
<LinkItem href="/page1" path={path}>
Page 1
</LinkItem>
)
}
Instead of undefined use either null or a empty string ""
Aswell. Use useState (not really needed in this scenario, but its always best to use in practice)
https://reactjs.org/docs/hooks-state.html
Well at the end the problem was the path variable which was undefined, and wasn't able to match href, so the condition never met.
Solution: call the path from useRouter hook with parameter .asPath, this gives me back the parameter which i stored in my path variable.
Code:
import NextLink from 'next/link'
import {useRouter} from "next/router";
const LinkItem = ({href, children, ...props}) => {
const path = useRouter().asPath
const active = path === href
return (
<NextLink href={href}>
<div className={active ? "<styles here>" : "<styles here>"}
{...props}
>
{children}
</div>
</NextLink>
)
}
Nav bar code:
const Navbar = props => {
const {path} = props
return (
<LinkItem href="/page1" path={path}>
Page 1
</LinkItem>
<LinkItem href="/page2" path={path}>
Page 2
</LinkItem>
)
}

First, the page is rendered, and only then the data comes in, but the old data remains on the page

The transition was made through react-router. I am trying to make the first project using Redux and I ran into the problem that my page is loading data that is not needed at the moment.
To be more specific, I have 2 classes in one form for selecting 1 of 3 components and index 1 is attached to each, 2 and 3. In the console you can see that when clicked, Redux picks up the data in the Store.
After selecting any component, the button for going to the next page becomes available. After switching to it, I would like to display the already entered data in the corner, but a default value has already been entered there. I came to the conclusion that the second page, which is not available until the user chose the option, it is rendered before the selection and is not updated after that, that's why the default hangs.
Maybe there is an option to prohibit rendering until the flag takes a different value or something like that?
The picture shows that in the last file the value 0 is immediately taken and does not change anymore.
my code
import React, { Component } from 'react'
import { Card,CardGroup,Button,Navbar,Nav } from 'react-bootstrap'
import A2 from '../assets/A2example.png'
import A3 from '../assets/A3example.png'
import A4 from '../assets/A4example.png'
import {BrowserRouter, Route, Switch, Link, } from 'react-router-dom'
import ZakazPortretaSecond from '../pages/ZakazPortretaSecond';
import store from '../redux/store'
import { connect } from 'react-redux'
class ZakazPortreta extends Component {
constructor(props){
super(props)
this.state = {
choosen: 4
}
}
portretSize(a){
this.setState({
choosen: a
})
store.dispatch({
type: 'SET_FORMAT_BY',
payload: a,
})
console.log(store.getState())
}
render() {
return (
<div className = "ZakazDiv">
<h2>Выберите желаемый размер портрета:A{this.state.choosen}</h2><br/>
<Navbar.Toggle aria-controls = "responsive-navbar-nav">
<Link to="/ZakazPortretaSecond" >Далее</Link>
</Navbar.Toggle>
<CardGroup>
<Card>
<Card.Body className = "cards" onClick={()=> this.portretSize(4)}>
<Card.Title>А4</Card.Title>
<Card.Subtitle className="mb-2 text-muted">210×297 мм</Card.Subtitle>
<Card.Text>
<img src = {A4} width = "370" height = "550"/>
</Card.Text>
</Card.Body>
</Card>
<Card >
<Card.Body className = "cards" onClick={()=> this.portretSize(3)}>
<Card.Title>А3</Card.Title>
<Card.Subtitle className="mb-2 text-muted">297 x 420 мм</Card.Subtitle>
<Card.Text>
<img src = {A3} width = "370" height = "550"/>
</Card.Text>
</Card.Body>
</Card>
<Card >
<Card.Body className = "cards" onClick={()=> this.portretSize(2)}>
<Card.Title>А2</Card.Title>
<Card.Subtitle className="mb-2 text-muted"> 420 x 594 мм</Card.Subtitle>
<Card.Text>
<img src = {A2} width = "370" height = "550"/>
</Card.Text>
</Card.Body>
</Card>
</CardGroup>
<BrowserRouter >
<div>
<Nav />
<Route path="/ZakazPortretaSecond" component={ZakazPortretaSecond} />
</div>
</BrowserRouter >
</div>
)
}
}
const mapStateToProps = (state) => {
console.log("Actual value",store.getState())
return{
format: state.format,
}
}
export default connect(mapStateToProps)(ZakazPortreta)
and second file
import React, { Component } from 'react'
import store from '../redux/store'
console.log("inside last file",store.getState())
class ZakazPortretaSecond extends Component {
render() {
return (
<div>
<h2 className='textSecond text-center'>Вы выбрали:A{store.format}</h2>
</div>
)
}
}
export default ZakazPortretaSecond
I solved the problem by updating the information separately on the page on load
Worked code:
import React, { Component } from 'react'
import store from '../redux/store'
console.log("inside last file",store.getState())
const refreshInf = () => {
let formats = store.getState().format
console.log(formats)
return formats
}
class ZakazPortretaSecond extends Component {
render() {
return (
<div onLoad = {refreshInf()}>
<h2 className='textSecond text-center'>Вы выбрали:A{refreshInf()}</h2>
</div>
)
}
}
export default ZakazPortretaSecond
function refreshInf on div has been refresh information in store!

Learning React Beautiful DnD basics without building Trello Clone

I want to learn React Beautiful Dnd by coding two div boxes that contain child elements I can drag between them. All (most?) tutorials online are Trello clones with loops and state that clutter my understanding of the basics. I want to simplify my understanding by hard coding only the most minimal state required with the end result being two components (the component name is "Column") each that contain a div (A component named "Task") that I can drag to the other.
At the moment. I am getting the error " TypeError: children is not a function ".
Here is my code:
src/App.js
import {DragDropContext} from 'react-beautiful-dnd';
import Column from './Components/Column';
function App() {
return (
<DragDropContext onDropEnd={result => console.log("my life is worth more than this")}>
<Column id="1"/>
</DragDropContext>
);
}
export default App;
src/Components/Column
import React from 'react';
import {Droppable} from 'react-beautiful-dnd';
import Task from "../../Components/Task"
function Column(props){
const { classes, id } = props;
let style = {
backgroundColor:"orange",
height:"100px",
width:"100px",
margin:"100px"
}
return (
<Droppable droppable = {id}>
{provided => (
<div {...provided.droppableProps} ref={provided.innerRef} style={style}>
Column is orange task is red
<Task id="1"/>
<Task id="2"/>
{provided.placeholder}
</div>
)
}
</Droppable>
)
}
export default Column
src/Components/Task
import React from 'react';
import {Draggable} from 'react-beautiful-dnd';
function Task(props){
const { classes, id } = props;
return (
<Draggable draggableId ={id}>
<div>
some task
</div>
</Draggable>
)
}
export default Task
Here is a basic working example of simply dragging items (in case anyone is looking for it). I've decided to document my learning here because I feel remedial examples are lacking in the official docs. I like "hello world" examples.
The first thing to realize is that using the library requires understanding three components. Each component has it's respective boilerplate code.
They are (buried down the page) in the official docs.
<DragDropContext />
- Wraps the part of your application you want to have drag and drop enabled for
<Droppable />
- An area that can be dropped into. Contains components
<Draggable />
- What can be dragged around
Your app needs to be wrapped in a single DragDropContext (multiple DragDropContext are not supported). This example has minimal state.The id properties in the state objects are required (they can be named different but are required none the less).
src/App.js
import React,{useState} from 'react';
import {DragDropContext} from 'react-beautiful-dnd';
import Column from './Components/Column';
function App() {
const [listOne, setListOne] = useState([{id:"1", title:"Test-1"},{id:"2", title:"Test-2"}]);
const [listTwo, setListTwo] = useState([{id:"3", title:"Test-3"},{id:"4", title:"Test-4"}]);
return (
<DragDropContext onDropEnd={result => console.log(result)}>
<Column id="1" list = {listOne}/>
<Column id="2" list = {listTwo}/>
<div> context hello world </div>
</DragDropContext>
);
}
export default App;
<Droppable/> components nest <Draggable/> components. This returned function boiler plate code is required:
{provided => (
)}
Explanation for each property of provided is here
src/Components/Column
import React from 'react';
import {Droppable} from 'react-beautiful-dnd';
import Task from "../../Components/Task"
function Column(props){
const { classes, id, list } = props;
let style = {
backgroundColor:"orange",
height:"300px",
width:"400px",
margin:"100px"
}
console.log(list)
return (
<Droppable droppableId = {id}>
{provided => (
<div {...provided.droppableProps} ref={provided.innerRef} style={style}>
{list.map((val,index)=>{
return <Task id={val.id} key={index} index={index} title={val.title}/>
})}
{provided.placeholder}
</div>
)
}
</Droppable>
)
}
export default Column
src/Components/Task
import React from 'react';
import {Draggable} from 'react-beautiful-dnd';
function Task(props){
const { classes, id, index,title } = props;
let style = {
backgroundColor:"red",
}
return (
<Draggable draggableId ={id} index={index} >
{(provided) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<h4 style={style}>{title}</h4>
</div>
)}
</Draggable>
)
}
export default Task

How to Change a css property based on a state of another component

I'm building a web page with gatsby which is based in react, and I need my nav component changes his sticky position to relative or auto, every time that I open the modal of the gallery component..but I don't know how to approach and solve the problem. The nav component belongs to the layout component which is Gallery's parent component...Here are the components involved:
nav component:
import React, { Component } from 'react'
import { Location } from '#reach/router'
import { Link } from 'gatsby'
import { Menu, X } from 'react-feather'
import Logo from './Logo'
import './Nav.css'
export class Navigation extends Component {
state = {
active: false,
activeSubNav: false,
currentPath: false
}
componentDidMount = () =>
this.setState({ currentPath: this.props.location.pathname })
handleMenuToggle = () => this.setState({ active: !this.state.active })
// Only close nav if it is open
handleLinkClick = () => this.state.active && this.handleMenuToggle()
toggleSubNav = subNav =>
this.setState({
activeSubNav: this.state.activeSubNav === subNav ? false : subNav
})
render() {
const { active } = this.state,
{ subNav } = this.props,
NavLink = ({ to, className, children, ...props }) => (
<Link
to={to}
className={`NavLink ${
to === this.state.currentPath ? 'active' : ''
} ${className}`}
onClick={this.handleLinkClick}
{...props}
>
{children}
</Link>
)
return (
<nav className={`Nav ${active ? 'Nav-active' : ''}`}>
<div className="Nav--Container container">
<Link to="/" onClick={this.handleLinkClick}>
<div style={{ width: `40px`, margin:`0 20px`}}>
<Logo />
</div>
</Link>
<div className="Nav--Links">
<NavLink to="/">Home</NavLink>
<NavLink to="/contact/">Contacto</NavLink>
<div className={`Nav--Group ${this.state.activeSubNav === 'about' ? 'active' : '' }`} >
<span className={`NavLink Nav--GroupParent ${
this.props.location.pathname.includes('about') ||
this.props.location.pathname.includes('team') ||
this.props.location.pathname.includes('news')
? 'active'
: ''
}`}
onClick={() => this.toggleSubNav('about')}
>
Nosotros
</span>
<div className="Nav--GroupLinks">
{subNav.map( (link, index)=> (
<NavLink
to={link.link}
key={'posts-subnav-link-' + index}
className="Nav--GroupLink">{link.name}</NavLink>
))}
</div>
</div>
</div>
<button
className="Button-blank Nav--MenuButton"
onClick={this.handleMenuToggle}
>
{active ? <X /> : <Menu />}
</button>
</div>
</nav>
)
}
}
export default ({ subNav }) => (
<Location>{route => <Navigation subNav={subNav} {...route} />}</Location>
)
the default position property is set to sticky in the nav.css file I want remove that and change it
dynamically depending of the modal gallery state, open or close.
this is my gallery component:
import React, { useState, useCallback } from "react";
import Gallery from "react-photo-gallery";
import Carousel, { Modal, ModalGateway } from "react-images";
const PhotoGallery = ({photos}) => {
const [currentImage, setCurrentImage] = useState(0);
const [viewerIsOpen, setViewerIsOpen] = useState(false);
const openLightbox = useCallback((event, { photo, index }) => {
setCurrentImage(index);
setViewerIsOpen(true);
}, []);
const closeLightbox = () => {
setCurrentImage(0);
setViewerIsOpen(false);
};
return(
<div>
<Gallery photos={photos} onClick={openLightbox} />
<ModalGateway>
{viewerIsOpen ? (
<Modal onClose={closeLightbox}>
<Carousel
currentIndex={currentImage}
views={photos.map(x => ({
...x,
srcset: x.srcSet,
caption: x.title
}))}
/>
</Modal>
) : null}
</ModalGateway>
</div>
)
}
export default PhotoGallery
the problem is that when the modal is open the nav still sticky and does not allow me access to the modal controls, like close and expand...and I need to change that.
There are a few approaches to this.
Old school classname toggling
Pass a prop down to the child component that reflects the state. On the child, use that prop to conditionally render one or more classes that represent the desired presentation.
Assign styles via style prop
This is similar to #1, but eliminates a layer of abstraction. Instead of assembling a class list you just assemble the CSS styles you'd like to apply as an object.
const Component = ({ someState }) =>
<div style={someState ? { border: "5px solid red" } : { color: "#999" }}>
Some Text
</div>
Use a CSS-in-JS library
The downside of the above approach is that you wind up duplicating styles for each instance of your element on the page. CSS-in-JS libraries solve this by extracting your styles into an automatically generated class and applying the class to your component instead. I prefer Emotion, but there are others.
Using Emotion you're able to accept a className prop from the parent that override the defaults set by the child. This inversion-of-control is really powerful and solves many of the shortcomings with early CSS-in-JS approaches.
const ParentComponent = () => {
const [someState] = useState(false)
return <ChildComponent css={{ color: someState ? "blue" : "red" }} />
}
const ChildComponent = ({ className }) =>
<div
css={{
color: "#000",
border: "4px solid currentColor"
}}
className={className}
>
Some Text
</div>
In the above example, className is assigned by Emotion using the generated class name assigned based on the css prop passed to ChildComponent inside of ParentComponent. The result of this would be a div with a blue border and blue text when someState is false (default). When someState is switched to true, the border and text will be red. This is because the styles passed in via className will override the styles assigned directly via css in Emotion.

React - sending props to component

I'm using React or Gatsby for a static website. A subpages need to send an prop or a variable(bool) to the main layout component, to determinme if we show a Hero image or not.
I got the following code (simplified) for the page:
import React from 'react'
import { graphql } from 'gatsby'
import Layout from '../components/layout'
import dividerIcon from '../images/hair-cut-tool.svg'
const IndexPage = ({ data }) => (
<Layout showHero={true}>
<div className="divider-wrapper">
<div className="divider">
<img alt="divider" src={dividerIcon} />
</div>
</div>
</Layout>
)
export default IndexPage
How can I "get" the prop in may Layout.js?
I'm sending it with "" but I have no idea, how to get this variable and use it.
As for right now the Layout.js looks like this:
const Layout = ({ children }) => (
<StaticQuery
query={graphql`
query SiteTitleQuery {
site {
siteMetadata {
title
}
}
heroImgLogo: file(relativePath: { eq: "logo.png" }) {
childImageSharp {
fixed(width: 300) {
...GatsbyImageSharpFixed_withWebp_noBase64
}
}
}
}
`}
render={data => (
<>
<div className="site">
{(children.showHero) ?
<Hero logoImg={data.heroImgLogo.childImageSharp.fixed} />
:
null }
<div className="site-content container">{children}</div>
</div>
</>
)}
/>
);
Layout.propTypes = {
children: PropTypes.node.isRequired,
}
export default Layout
yet again, simplified.
I tried to children.showHero but it wasn't the right approach, I guess.
Any hints?
You can destructure it alongside children:
const Layout = ({ children, showHero }) => (
Be sure to replace children.showHero with just showHero.

Categories