Reusing React 'key' - javascript

I want to know it's normal to reuse react key from component to component. In Row component i got key From column component and reuse it for mapping Row childrens
const Table = props => {
const { data, children, showHeader } = props;
return (
<div className="table">
{showHeader && <TableHead children={children} />}
{data.map(data => {
return <Row key={data.id} data={data} children={children} />;
})}
</div>
);
};
const TableHead = props => {
const { children } = props;
return (
<div className="table-row">
{children.map(col => {
const { title, field, width } = col.props;
return (
<div className="table-col" key={field}>
<div
className="table-cell table-cell-head"
style={{
width: width
}}
>
{title}
</div>
</div>
);
})}
</div>
);
};
//HERE I GET key from Column component and reuse it for rows (number of columns === number of rows)
const Row = props => {
const { data, children } = props;
return (
<div className="table-row">
{children.map(col => {
let { field, ...rest } = col.props;
const { key } = col;
return (
<div className="table-col" key={key}>
{React.cloneElement(col, {
...rest,
field,
data
})}
</div>
);
})}
</div>
);
};
const Column = props => {
const { field, data, width } = props;
return (
<div
style={{
width: width
}}
className="table-cell"
key={data.id}
>
{data[field]}
</div>
);
};
const HeadRow = props => {
const { children } = props;
return (
<div className="table-row">
{children.map(col => {
const { title, field, width } = col.props;
return (
<div className="table-col" key={field}>
<div
className="table-cell table-cell-head"
style={{
width: width
}}
>
{title}
</div>
</div>
);
})}
</div>
);
};
const initData = [
{
id: 1,
name: "Ivan",
age: "My age is 27",
enabled: true,
myListValue: [
{
myName: "Duncan",
myDescription: "Immortal!",
myGroup: "Immortal",
createDate: "2019-08-12T05:21:28Z"
}
],
lastChanged: new Date(),
sortable: true
},
{
id: 2,
name: "Vitaly",
age: `My age is 25\nMy age is 25\nMy age is 25\n`,
lastChanged: new Date(),
enabled: true,
sortable: true
},
{
id: 3,
name: "Sergey",
age: "My age is 29",
enabled: true,
myListValue: [
{
myName: "Duncan",
myDescription: "Immortal!",
myGroup: "Immortal",
createDate: "2019-08-12T05:21:28Z"
},
{
myName: "Connor",
myDescription: "Immortal2!",
myGroup: "MacLeods",
createDate: "2019-08-12T05:21:28Z"
},
{
myName: "John Snow",
myDescription: "(he knows nothing)",
myGroup: "WhiteWalkers",
createDate: "2019-08-12T05:21:28Z"
},
{
myName: "Jamie Lannister",
myDescription: "Lannisters always pay their debts",
myGroup: "Red castle",
createDate: "2019-08-12T05:21:28Z"
}
],
lastChanged: new Date()
}
];
ReactDOM.render(
<Table data={initData} showHeader={true} sortableConfig={{}}>
<Column key="0" field="name" width={150} title="Name" sortable="true" />
<Column key="1" field="age" width={150} title="AGe" sortable="true" />
</Table>, document.getElementById('root'))
.App {
font-family: sans-serif;
text-align: center;
display: flex;
flex-wrap: wrap;
}
.table {
border: 1px solid;
border-bottom: none;
}
.table-row {
width: 100%;
border-bottom: 1px solid;
display: flex;
}
.table-col {
border-right: 1px solid;
}
.table-col:last-child {
border: none;
}
.table-cell {
white-space: pre-line;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
padding: 10px;
box-sizing: border-box;
}
.table-cell-head {
font-weight: bold;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Keys help React identify which items have changed, are added, or are
removed.
Keys should be given to the elements inside the array to give them a stable identity, and they only make sense in the context of the surrounding array, so it doesn't matter if two isolated lists have elements with equal keys as long as they are not equal inside the same list (repeated ids). There is no problem with the following code
{
arr.map((item,index) =>{
//defining key only once per `arr` iteration
const key = uniqueId(index)
return(
<div key={key}>
{
arr2.map((item, index) => <span key={key} />)
}
</div>
)
})
}
Note that the key is only relevant within a particular parent React element. React won’t try to “match up” elements with the same keys between different parents. (React doesn’t have idiomatic support for moving a host instance between different parents without re-creating it.) Font
See the docs here

Related

React.js - How to check if the div is at the bottom of the screen (dynamically)

Is there any way to check if the div is at the bottom of the another div (acting as a parent, or container).
What I tried
So basically I made demo where there are child elements (items, setItems) in the div that can be added and deleted and also you can change the height of them by clicking on the divs (important here). Also there is another div, which is not in the items state, where I want to change the title of that item, if it is at the bottom of the his parent div (also items have the same parent as this div has).
Problem with my solution
I have tried something where I am looking at the getBoundingClientRect() of the parent container and this "blue" div, lets call it like that, and it will work fine, ONLY IF the items have the same height, but soon as a delete the one item and change the height of it by clicking on the div, it will not work. It will show that it is on the bottom of the screen (the title will be true) but in reality it is not.
My code
App.js - only for demo purposes
import "./styles.css";
import { useState, useEffect, useRef } from "react";
export default function App() {
const arrayItems = [
{
id: 1,
name: "test",
resized: false
},
{
id: 2,
name: "test1",
resized: false
},
{
id: 3,
name: "test2",
resized: false
}
];
const [items, setItems] = useState(arrayItems);
const [title, setTitle] = useState(false);
const parentRef = useRef(null);
const itemsRef = useRef(null);
useEffect(() => {
if (
parentRef?.current.getBoundingClientRect().bottom -
itemsRef?.current.getBoundingClientRect().height <=
itemsRef?.current.getBoundingClientRect().top
) {
setTitle(true);
} else {
setTitle(false);
}
}, [parentRef, items]);
const handleClick = () => {
const maxValue = Math.max(...items.map((item) => item.id)) + 1;
setItems((prev) => [
...prev,
{ id: maxValue, name: "testValue", resized: false }
]);
};
const handleDelete = () => {
setItems((prev) => prev.slice(0, prev.length - 1));
};
const handleResize = (item) => {
setItems((prev) =>
prev.map((itemOld) => {
if (itemOld.id === item.id) {
return itemOld.resized === true
? { ...itemOld, resized: false }
: { ...itemOld, resized: true };
} else {
return itemOld;
}
})
);
};
console.log(items);
return (
<div className="App">
<button onClick={handleClick}>Add new</button>
<button onClick={handleDelete}>Delete last</button>
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<div ref={parentRef} className="container">
{items?.map((item) => {
return (
<div
onClick={() => handleResize(item)}
style={{ height: item.resized ? "70px" : "20px" }}
key={item.id}
className="container-item"
>
<p>{item.name}</p>
</div>
);
})}
<div ref={itemsRef} id="title-div">
{title ? "At the bottom" : "Not at the bottom"}
</div>
</div>
</div>
);
}
styles.css
.App {
font-family: sans-serif;
text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 1rem;
}
* {
margin: 0;
padding: 0;
}
.container {
width: 600px;
height: 300px;
background-color: gray;
display: flex;
flex-direction: column;
}
.container-item {
width: 100%;
background-color: hotpink;
}
#title-div {
width: 100%;
padding: 1rem;
background-color: blue;
color: white;
}
What I want to make
As the title suggest I want to see if the div is at the bottom of the container/parent div. That is it, and other items in that parent div, cannot interfere with this div, in sense that adding, resizing, deleting those items, will not suddenly change the position of the div that I want to analyse (to see if it is at the bottom of the screen)
I have come up with my own solution and it works always. I just have to deduct the "top" from parentsRef and "top" from the itemsRef, and add to that the clientHeight of the itemsRef. This way it will always be at the bottom of the container, doesnt matter if I delete the items, resize them etc.
The code
useEffect(() => {
if (
parentRef?.current.clientHeight <=
itemsRef?.current.getBoundingClientRect().top -
parentRef?.current.getBoundingClientRect().top +
itemsRef?.current.clientHeight
) {
setTitle(true);
} else {
setTitle(false);
}
}, [parentRef, items, itemsRef]);

Best way to Iterate trough an Array with React

I want to make a simple memory game with react for a neat portfolio project. I have an array of 20 items and 10 sets of colors
const data = [
// Manchester Blue
{
ID: 1,
Color: "#1C2C5B"
},
{
ID: 2,
Color: "#1C2C5B"
},
// Seafoam Green
{
ID: 3,
Color: "#93E9BE."
},
{
ID: 4,
Color: "#93E9BE."
},
// Candy Apple Red
{
ID: 5,
Color: "#ff0800"
},
{
ID: 6,
Color: "#ff0800"
},
// Lake Placid Blue
{
ID: 7,
Color: "#3f5c9"
},
{
ID: 8,
Color: "#3f5c9"
},
// Safety Orange
{
ID: 9,
Color: "#ff6700"
},
{
ID: 10,
Color: "#ff6700"
},
// Vivid Orchard
{
ID: 11,
Color: "#cc00ff"
},
{
ID: 12,
Color: "#cc00ff"
},
// Apple Green
{
ID: 13,
Color: "#8db600"
},
{
ID: 14,
Color: "#8db600"
},
// Old Moss Green
{
ID: 15,
Color: "#867e36"
},
{
ID: 16,
Color: "#867e36"
},
// Canary Yellow
{
ID: 17,
Color: "#ffef00"
},
{
ID: 18,
Color: "#ffef00"
},
// Pink
{
ID: 19,
Color: "#ff1493"
},
{
ID: 20,
Color: "#ff1493"
}
];
I have the following basic HTML and CSS
HTML
<div>
<div className='flexbox-container'>
<div id='1' className='flexbox-item-one'></div>
<div id='2' className='flexbox-item-one'></div>
<div id='3' className='flexbox-item-one'></div>
<div id='4' className='flexbox-item-one'></div>
<div id='5' className='flexbox-item-one'></div>
</div>
<div className='flexbox-container'>
<div id='6' className='flexbox-item-two'></div>
<div id='7' className='flexbox-item-two'></div>
<div id='8' className='flexbox-item-two'></div>
<div id='9' className='flexbox-item-two'></div>
<div id='10' className='flexbox-item-two'></div>
</div>
<div className='flexbox-container'>
<div id='11' className='flexbox-item-three'></div>
<div id='12' className='flexbox-item-three'></div>
<div id='13' className='flexbox-item-three'></div>
<div id='14' className='flexbox-item-three'></div>
<div id='15' className='flexbox-item-three'></div>
</div>
<div className='flexbox-container'>
<div id='16' className='flexbox-item-four'></div>
<div id='17' className='flexbox-item-four'></div>
<div id='18' className='flexbox-item-four'></div>
<div id='19' className='flexbox-item-four'></div>
<div id='20' className='flexbox-item-four'></div>
</div>
</div>
CSS
.flexbox-item-one {
width: 100px;
height: 100px;
border: 3px solid black;
background-color: darkcyan;
margin: 1.5rem;
}
.flexbox-item-two {
width: 100px;
height: 100px;
border: 3px solid black;
background-color: greenyellow;
margin: 1.5rem;
}
.flexbox-item-three {
width: 100px;
height: 100px;
border: 3px solid black;
background-color: slateblue;
margin: 1.5rem;
}
.flexbox-item-four {
width: 100px;
height: 100px;
border: 3px solid black;
background-color: magenta;
margin: 1.5rem;
}
.flexbox-container {
display: flex;
justify-content: center;
}
I also have done some research and procured the following way to shuffle the array.
let shuffledColors = colors
.map(value => ({ value, sort: Math.random() }))
.sort((a, b) => a.sort - b.sort)
.map(({ value }) => value)
So my React skills are just not to the level where I can build this. I want to have an useEffect hook that loads the array when the project is rendered and uses the shuffle method. It would then have to dynamically add the colors to the HTML elements. SOmething which I'm not entirely sure how to do as well. Maybe an ForEach statement that uses the document.GetElementByID to add the css dynamically.
I would then have to get a way with positions to have two divs on top of each other which I can have the top one dynamically have its opacity altered on a onClick event to show the 'real colors'
Then there would have to be a check if the ID's of the colors match and a counter which keeps track of how many matches have been made.
For now I just want to get my head around how to have this array of colors randomised and assigned to the divs when loaded with the useEffect hook.
Ok, let's do it.
First, create a FlexboxContainer component that return a div with flexbox-container className:
const FlexboxContainer = ({ children }) => {
return <div className="flexbox-container">{children}</div>;
};
Next, create a FlexboxItem component, where the show/hide logic resides. Here, we don't need a stack of two divs. Instead, we use conditional backgroundColor styling that depends on actualColor state on a div. Here, we do have a onClick handler that would set the actualColor by using setActualColor setter once the user clicks the color card div. The show/hide (toggle) action would be based on the previous state of actualColor.
const FlexboxItem = ({ id, color, group }) => {
const [actualColor, setActualColor] = useState();
const handleClick = () => {
setActualColor((prevState) => (!prevState ? color : undefined));
};
return (
<div
id={id}
onClick={handleClick}
className={`flexbox-item-${group}`}
style={actualColor ? { backgroundColor: color } : null}
></div>
);
};
At the main component, we need to define currentColors state where we put our shuffledColors.
const [currentColors, setCurrentColors] = useState();
and we use useCallback hooks on shuffledColors
const shuffledColors = useCallback(() => {
return data
.map((value) => ({ value, sort: Math.random() }))
.sort((a, b) => a.sort - b.sort)
.map(({ value }) => value);
}, []);
since the shuffledColors going to be used inside a memoized groups constant where we re-structure the shuffledColors into groups of colors.
The aim of this grouping is to provide an array that is going to be used to render a dynamics group of five color divs by using the iteration of FlexboxContainer component.
const groups = useMemo(() => {
if (!currentColors) return [];
const chunkSize = 5;
const groupedItems = [];
for (let i = 0; i < currentColors.length; i += chunkSize) {
const chunk = currentColors.slice(i, i + chunkSize);
groupedItems.push(chunk);
}
return groupedItems;
}, [currentColors]);
...
return (
...
<div>
{currentColors &&
groups.map((items, index) => {
return (
<FlexboxContainer key={index}>
{items.map((item) => {
return (
<FlexboxItem
id={item.id}
color={item.color}
group={groupColors[index]}
key={item.id}
/>
);
})}
</FlexboxContainer>
);
})}
</div>
...
)
}
Next, we define a shuffleColors method, by which we can use it to set the initial colors inside a useEffect hook.
const shuffleColors = useCallback(() => {
const colors = shuffledColors();
setCurrentColors(colors);
}, [shuffledColors]);
useEffect(() => {
shuffleColors();
}, [shuffleColors]);
If we want to re-shuffle the colors, just call it from onCLick handler. Here we do a minor modification at FlexboxItem component and add a currentColor watcher.
const FlexboxItem = ({ id, color, group, show }) => {
const [actualColor, setActualColor] = useState();
// add show props change watcher
useEffect(() => {
setActualColor(show ? color : undefined);
}, [color, show]);
...
}
...
// current color watcher will reset showAll state on any currentColors state change
useEffect(() => {
setShowAll(false);
}, [currentColors]);
return (
...
<button onClick={shuffleColors}>Re-shuffle Colors</button>
...
)
It does not fully answer your question, at least, will give you new insight into how React works to achieve your objectives.
Here is the complete code of my explanation:

How can I use the same setState for two different tasks?

EDIT*** THE CODE IN THE SNIPPET IS NOT MEANT TO BE RUN BUT ONLY DISPLAY MY CODE, SORRY FOR THE CONFUSION
I am working a slider for an e-commerce app. Making each slider map over my useState array to create the different cards and the useState is receiving data from my fetched JSON server.
Now I am stuck at a brick wall in figuring how to use the same state to handle rendering the image onto my slider & click on the arrow to show the new slide.
I realize that I should update the state but I feel that I am over (or under) thinking things on how to do so.
My code snippets will show one, my code & two my JSON server data.
I receive an "uncaught error: too many re-renders." React limits number of renders to prevent an infinite loop
Can somebody please walk me through what I need to do here to complete this task? Thanks!
import {useState, useEffect} from 'react';
import { ArrowLeftOutlined, ArrowRightOutlined } from "#material-ui/icons";
import styled from "styled-components";
import { set } from 'react-hook-form';
const Container = styled.div`
width: 100%;
height: 95vh;
display: flex;
// background-color: #b3f0ff;
position: relative;
overflow: hidden;
`;
const Arrow = styled.div`
width: 50px;
height: 50px;
background-color: #e6ffff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
top: 0;
bottom: 0;
left: ${(props) => props.direction === "left" && "10px"};
right: ${(props) => props.direction === "right" && "10px"};
margin: auto;
cursor: pointer;
opacity: 0.5;
z-index: 2;
`;
const Wrapper = styled.div`
height: 100%;
display: flex;
transform: translateX(${props => props.slideIndx.data2 * -100}vw);
`
const Slide = styled.div`
width: 100vw;
height: 100vw;
display: flex;
align-items: center;
background-color: ${props => props.bg};
`
const ImgContainer = styled.div`
height: 100%;
flex:1;
`
const Image = styled.img`
padding-left: 30px;
align-items: left;
`
const InfoContainer = styled.div`
height: 80%;
flex:1;
padding: 30px;
`
const Title = styled.h1`
font-size: 50px
`
const Desc = styled.p`
margin: 50px 0px;
font-size: 20px;
font-weight: 500;
letter-spacing: 3px;
`
const Button = styled.button`
padding: 10px;
font-size: 20px;
background-color: transparent;
cursor: pointer;
`
const Slider = () => {
const [slideIndx, setSlideIndx] = useState({data1:[], data2: 0});
const handleClick = (direction) => {
if(direction === "left"){
setSlideIndx(slideIndx.data2 > 0 ? slideIndx.data2 - 1 : 2);
} else{
setArrowIndx(slideIndx.data2 < 2 ? slideIndx.data2 + 1 : 0);
}
};
const newArray = []
setSlideIndx((slideIndx) => ({
...slideIndx,
data2: [...newArray]
}));
const fetchSliderItems = (id) => {
fetch('http://localhost:3000/sliderItems')
.then(resp => resp.json())
.then(data => {
console.log(data)
setSlideIndx(data)
})
}
useEffect(() => {fetchSliderItems()}, [])
return (
<Container>
<Arrow direction="left" onClick={() => handleClick("left")}>
<ArrowLeftOutlined />
</Arrow>
<Wrapper slideIndx={slideIndx.data2}>
{slideIndx.data1.map((item) => (
<Slide bg={item.bg}>
<ImgContainer>
<Image src={item.img}/>
</ImgContainer>
<InfoContainer>
<Title>{item.title}</Title>
<Desc>{item.desc}</Desc>
<Button>SHOP NOW</Button>
</InfoContainer>
</Slide>
))}
</Wrapper>
<Arrow direction="right" onClick={() => handleClick("right")}>
<ArrowRightOutlined />
</Arrow>
</Container>
)
}
export default Slider
{
"sliderItems": [
{
"id": 1,
"img": "../images/model1.png",
"title": "SPRING CLEANING",
"desc": "DONT MISS OUR BEST COLLECTION YET! USE #FLATIRON10 TO RECEIVE 10% OFF YOUR FIRST ORDER",
"bg": "#b3ecff"
},
{
"id": 2,
"img": "../images/model2.png",
"title": "SHOW OFF HOW YOU DRESS",
"desc": "WITH OUR HUGE SELECTION OF CLOTHES WE FIT ALL YOUR STYLING NEEDS",
"bg": "#ccf2ff"
},
{
"id": 3,
"img": "../images/model3.png",
"title": "POPULAR DEALS",
"desc": "RECEIVE FREE SHIPPING ON ALL ORDERS OVER $50!",
"bg": "#fe6f9ff"
}
]
}
It is totally possible to do this with one useState, two useStates and my personal preference, useReducer.
I'll just give an example. I haven't tried running this code, as I don't have the components. So treat it like pseudo code:
const initialState = {
selectedIndex: 0,
"sliderItems": [
{
"id": 1,
"img": "../images/model1.png",
"title": "SPRING CLEANING",
"desc": "DONT MISS OUR BEST COLLECTION YET! USE #FLATIRON10 TO RECEIVE 10% OFF YOUR FIRST ORDER",
"bg": "#b3ecff"
},
{
"id": 2,
"img": "../images/model2.png",
"title": "SHOW OFF HOW YOU DRESS",
"desc": "WITH OUR HUGE SELECTION OF CLOTHES WE FIT ALL YOUR STYLING NEEDS",
"bg": "#ccf2ff"
},
{
"id": 3,
"img": "../images/model3.png",
"title": "POPULAR DEALS",
"desc": "RECEIVE FREE SHIPPING ON ALL ORDERS OVER $50!",
"bg": "#fe6f9ff"
},
],
}
function reducer(state, action) {
switch (action.type) {
case 'setData':
return {
selectedIndex: 0,
sliderItems: action.sliderItems,
};
case 'slideRight':
return {
...state,
// this assumes you want to go back to index 0 if you slide right on last item
selectedIndex: state.sliderItems.length - state.selectedIndex > 1 ? state.selectedIndex + 1 : 0,
};
case 'slideLeft':
return {
...state,
// this assumes you want to go to last item if sliding left on first item
selectedIndex: state.selectedIndex === 0 ? state.sliderItems.length - 1 : state.selectedIndex - 1,
};
default:
return state;
}
}
const Example = () => {
const [{ sliderItems, selectedIndex }, dispatch] = useReducer(initialState, reducer);
return (
<Container>
<Arrow direction="left" onClick={() => dispatch({ type: 'slideLeft' })}>
<ArrowLeftOutlined />
</Arrow>
<Wrapper slideIndx={selectedIndex}>
{sliderItems.map((item) => (
<Slide bg={item.bg}>
<ImgContainer>
<Image src={item.img}/>
</ImgContainer>
<InfoContainer>
<Title>{item.title}</Title>
<Desc>{item.desc}</Desc>
<Button>SHOP NOW</Button>
</InfoContainer>
</Slide>
))}
</Wrapper>
<Arrow direction="right" onClick={() => dispatch({ type: 'slideRight' })}>
<ArrowRightOutlined />
</Arrow>
</Container>
)
}
If you choose to try this approach, just dispatch a setData action in your fetch handler. That will reset the state with new items and start at index 0. The behavior can of course be modified to your liking.
dispatch({
type: 'setData',
sliderItems: fetchResult.sliderItems,
})

Reactjs, Styling each element of array with different styles

I want to create an array with some elements and then iterate (with .forEach or .map) it.
but each of array elements must have different style. for example I have an array like that:
["johnny red", "Stephanie blue", "Hans green", "Fifty Shades Of grey :d"]
also, I want reuse some of array's individual elements in another component.
how can i do that?
(imagine that last word in each element is a individual style e.g. background-color or color,)
Can you help me?
please give me an advice, what would I do?
There are many ways to achieve this, the key difference is how you are representing your value-style pairs.
Eventually, you need to map the values to their styles.
For example:
const myStyles = {
'johnny red': { color: 'red' },
'Stephanie blue': { color: 'blue' },
'Hans green': { color: 'green' },
'Fifty Shades Of grey :d': { color: 'pink' }
};
export default function App() {
return (
<FlexBox>
<FlexItem>
{Object.entries(myStyles).map(([key, style]) => (
<div key={key} style={style}>
{key}
</div>
))}
</FlexItem>
</FlexBox>
);
}
It's not very clear what you want, but you can attach styles to components like this:
const values = [
{
text: "one",
css: { color: 'blue' }
},
{
text: "two",
css: { color: 'red' }
},
];
const Test = props => {
const { values } = props;
return (
<div>{values.map(
(value, i) => <div key={i} style={value.css}>{value.text}</div>
)}</div>
);
}
ReactDOM.render(
<Test values={values} />,
document.getElementById("react")
);
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
You mean something like this? The snippet is below and here is the codepen texample: https://codepen.io/tormod17/pen/apvXoW?editors=0010
Also, have you considered incorporating styled components with react? It could help with this project. https://www.styled-components.com/docs/basics
{children.map(element => {
return (
<element.type
{...element.props}
style={{
height: '33%',
}}
/>
)
})}
This is from another project:
import * as React from "react";
import { IAddressListProps } from '../../detailPage/components/interfaces/IAddressListProps';
import { config } from "../../config";
export class PrintAddressList extends React.Component<IAddressListProps, {}>{
constructor(props: IAddressListProps) {
super(props);
}
private createAddressCard = () => {
let parent = [];
this.props.items.map((address) => {
parent.push(<div style={{ padding: '10px', border: '1px solid #ccc', margin: '5px' }}>
<label>{config.colNames.addressList.clmnAddressType}: </label>
<label>{address.Adressart}</label><br />
<label>{config.colNames.addressList.clmnCompanyName}: </label>
<label>{address.Firma}</label><br />
<label>{config.colNames.addressList.clmnPlaceName}: </label>
<label>{address.Ort}</label><br />
<label>{config.colNames.addressList.clmnZipCode}: </label>
<label>{address.PLZ}</label><br />
<label>{config.colNames.addressList.clmnTelephone}: </label>
<label>{address.Telefon}</label><br />
</div>);
});
return parent;
}
public render(): React.ReactElement<IAddressListProps> {
return (
<div>
{this.createAddressCard()}
</div>
);
}
}

How do I prevent drop in originating container in react js and react-beautiful-dnd?

I am using React and react-beautiful-dnd.
This has had me stumped
My use case is to drag items from container1 to container2.
Items in Container1 cannot be dropped in Container 1 only in Container 2.
If an item in container 1 is dragging the remaining items in Container 1 should not move to allow a drop.
I created a sample fiddle to isolate the use case - https://codesandbox.io/s/34z92ny69p
Any help is appreciated
G
If I'm understanding what you want:
Allow Container 1 items to be moved into Container 2.
Do not allow Container 1 to be empty.
Do not allow any items to be moved back into Container 1.
Optional: I also set it up so that you can disable dragging by passing down an isDragDisabled prop in the DroppableContainer, for example:
<DroppableContainer
droppableId="Container2"
title="Container 2"
data={this.state.container2Data}
isDragDisabled
/>
Working example: https://codesandbox.io/s/moy02o60yx
components/Workspace.js
import React, { Component } from "react";
import { DragDropContext } from "react-beautiful-dnd";
import { Grid, Row } from "react-bootstrap";
import DroppableContainer from "./DroppableContainer";
const testData = {
container1Data: [
{ id: 1, name: "item 1" },
{ id: 2, name: "item 2" },
{ id: 3, name: "item 3" },
{ id: 4, name: "item 4" },
{ id: 5, name: "item 5" },
{ id: 6, name: "item 6" }
],
container2Data: [
{ id: 101, name: "other item 1" },
{ id: 102, name: "other item 2" }
]
};
export default class Workspace extends Component {
state = {
container1Data: testData.container1Data,
container2Data: testData.container2Data
};
onDragEnd = ({ destination, source }) => {
if (
!destination ||
destination.droppableId !== "Container2" ||
(destination.droppableId === source.droppableId &&
destination.index === source.index)
) {
return;
}
this.setState(prevState => {
const addItem = prevState.container1Data.splice(source.index, 1);
prevState.container2Data.splice(destination.index, 0, ...addItem);
return {
container1Data: [...prevState.container1Data],
container2Data: [...prevState.container2Data]
};
});
};
render = () => (
<DragDropContext onDragEnd={this.onDragEnd}>
<Grid bsClass="box-container">
<Row>
<DroppableContainer
droppableId="Container1"
title="Container 1"
data={this.state.container1Data}
dropDisabled
/>
<DroppableContainer
droppableId="Container2"
title="Container 2"
data={this.state.container2Data}
/>
</Row>
</Grid>
</DragDropContext>
);
}
components/DroppableContainer.js
import React, { PureComponent } from "react";
import { Droppable } from "react-beautiful-dnd";
import { Col } from "react-bootstrap";
import styled from "styled-components";
import DraggableItem from "./DraggableItem";
const StyledDiv = styled.div`
border: 1px solid #000080;
padding: 15px;
`;
export default class DroppableContainer extends PureComponent {
renderDraggableItems = () =>
this.props.data.map((item, i) => (
<DraggableItem
key={i}
item={item}
index={i}
isDragDisabled={
this.props.isDragDisabled || this.props.data.length === 1
}
/>
));
render = () => (
<Col sm={4}>
<Droppable
droppableId={this.props.droppableId}
isDropDisabled={this.props.dropDisabled || false}
>
{provided => (
<StyledDiv
className={`container ${this.props.data.length === 1 ? "disabled" : null }`}
ref={provided.innerRef}
{...provided.droppableProps}
>
<div className="row">
<div className="col">{this.props.title}</div>
</div>
{this.renderDraggableItems()}
{provided.placeholder}
</StyledDiv>
)}
</Droppable>
</Col>
);
}
components/DraggableItem.js
import React from "react";
import { Draggable } from "react-beautiful-dnd";
import { Col } from "react-bootstrap";
import styled from "styled-components";
const DragItemStyled = styled.span`
text-transform: uppercase;
outline: none;
border: 0;
background-color: ${props => (props.isDragDisabled ? "#d8d8d8" : "#bec7bd")};
line-height: 32px;
color: ${props => (props.isDragDisabled ? "#a9a9a9" : "#000080")};
display: inline-block;
font-family: Karla, Verdana, sans-serif;
font-size: 14px;
padding-left: 15px;
padding-right: 10px;
cursor: ${props => (props.isDragDisabled ? "no-drop" : "grab")};
border-radius: 5px;
margin-bottom: 5px;
width: 150px;
`;
const DraggableItem = ({ item, index, isDragDisabled }) => (
<Draggable
key={item.id}
draggableId={JSON.stringify({
nodeId: item.id,
type: "DragItem"
})}
index={index}
isDragDisabled={isDragDisabled}
>
{provided => (
<div
className="row"
{...provided.draggableProps}
{...provided.dragHandleProps}
ref={provided.innerRef}
>
<Col md={4}>
<DragItemStyled isDragDisabled={isDragDisabled}>
{item.name}
</DragItemStyled>
</Col>
</div>
)}
</Draggable>
);
export default DraggableItem;

Categories