React-Spring: Trail inside UseTransition map not working - javascript

I'm a newbie trying to use React Spring in my React (16.8) project. I made a little carousel which works, but I want to add a credit at the bottom of each slide which should stagger in. A credit consist of two items and the second should animate in a tad slower than the first.
For this I want to use Trail but while the contents of the Trail renders, the animation does not fire.
This is my code so far:
const slides = [
{
id: 0,
desktopImage: "http://lorempixel.com/400/200/",
title: "foo",
studio: "bar"
},
{
id: 1,
desktopImage: "http://lorempixel.com/400/200/",
title: "foo",
studio: "bar"
},
{
id: 2,
desktopImage: "http://lorempixel.com/400/200/",
title: "foo",
studio: "bar"
}
];
const [slideIndex, set] = useState(0);
useEffect(
() =>
void setInterval(() => set(state => (state + 1) % slides.length), 4000),
[]
);
const transitions = useTransition(slides[slideIndex], item => item.id, {
from: { opacity: 0, left: -10 },
enter: { opacity: 1, left: -100 },
leave: { opacity: 1, left: -100 },
config: { friction: 25, duration: 4000 }
});
return (
<div className="header">
{transitions.map(({ item, props, key }) => {
let bgImg = item.desktopImage;
const creditArray = [item.title, item.studio];
return (
<div key={key}>
<animated.div
className="bg"
style={{ ...props, backgroundImage: `url(${bgImg})` }}
/>
<div className="imageCredit">
{
<Trail
key={key}
items={creditArray}
keys={key}
from={{ opacity: 0, transform: "translate3d(-100, 0, 0)" }}
to={{ opacity: 1, transform: "translate3d(0, 0, 0)" }}
>
{item => props => <div className="h4">{item}</div>}
</Trail>
}
</div>
</div>
);
})}
</div>
);

Figured it out, I forgot to pass downn the actual style down to the rendered element.
so instead of :
<Trail
key={key}
items={creditArray}
keys={key}
from={{ opacity: 0, transform: "translate3d(-100, 0, 0)" }}
to={{ opacity: 1, transform: "translate3d(0, 0, 0)" }}>
{item => props => <div className="h4">{item}</div>}
</Trail>
I needed to do:
<Trail
items={creditArray}
keys={item => item + key}
from={{opacity: 0, transform: 'translateX(100px)'}}
to={{opacity: 1, transform: 'translateX(0)'}}>
{item => style => (
<animated.div style={{ ...style }} className="h4">
{item}
</animated.div>
)}
</Trail>

Related

How to make dynamic array of color in React?

I'm working on one project and I'm a bit lost in checking the color chosen and show it correctly.
I'm using React and I have a component which gets currentPattern, currentColor, currentFarbeColor, setCurrentFarbeColor.
export default function SecondDropdownSidebar({
currentPattern,
currentColor,
currentFarbeColor,
setCurrentFarbeColor,
onClose,
}) {
I just need currentColor from this because I'm getting it from another component and add it to color variable with data I have on ContextAPI as
const color = data.selectedColors[currentColor];
console.log(color); //returns {first: "#12ff90", second: "#4f4092"}
Then I have array with first element of color object.
const [currentCuffsCollar, setCurrentCuffsCollar] = useState(0);
const [currentKnitting, setCurrentKnitting] = useState(0);
const [CUFFS_COLLARS, setCuffCollars] = useState([
{
text: "Bündchen unten",
color: color !== undefined ? color.first : null,
knitting: null,
icon: "itoito_choose_cuff_bottom.svg",
value: 0,
},
{
text: "Kragen",
color: null,
knitting: null,
icon: "itoito_choose_collar.svg",
value: 1,
},
]);
const KNITTINGS = [
{
text: "Rip 1-1",
icon: "rip1-1.jpg",
value: 0,
},
{
text: "Rip 2-1",
icon: "rip2-1.jpg",
value: 1,
},
{
text: "Schlauch",
icon: "schlauch.jpg",
value: 2,
},
{
text: "Rollkante",
icon: "rollkante.jpg",
value: 3,
},
];
I tried having color object as array:
if (color !== undefined) keys = Object.values(color);
if (color !== undefined && Math.random() > 0.5) {
changeBottomPartColor(color.first);
changeNeckPartColor(color.second);
setCuffCollars(
CUFFS_COLLARS.map((object) => {
if (object.value === 0) {
return { ...object, color: color.first };
} else if (object.value === 1) {
return { ...object, color: color.second };
}
return object;
})
);
}
and click event:
const onClicked = (color) => {
setIsTrue(false);
setCurrentFarbeColor(color);
setCuffCollars(
CUFFS_COLLARS.map((object) => {
if (object.value === currentCuffsCollar) {
return { ...object, color: color };
}
return object;
})
);
if (currentCuffsCollar === 0) {
changeBottomPartColor(color);
} else if (currentCuffsCollar === 1) {
changeNeckPartColor(color);
}
};
and finally my main code for selecting color is like following:
{color ? (
<ul>
{CUFFS_COLLARS.map((item, index) => (
<li
key={item.value}
className={
keys.includes(item.color)
? styles.active
: null
}
onClick={() => onClicked(keys[index])}
>
<span>
<span
style={{
backgroundColor: keys[index],
}}
></span>
</span>
</li>
))}
</ul>
) : (
<h5 style={{ margin: 0, color: "#a2a2a2", fontWeight: 400 }}>
No color has been chosen
</h5>
)}
The problem is that className should get right condition to show only 1 color as chosen, but now it retuns both because it considers 1st and 2nd CUFF_COLLAR. Following image is result at the moment I have, but it should have only 1 selected color. The same thing should be applied to KNITTINGS, so basically every pattern should have their own color and knittings selected out of those.

How can I create a menu with my json info with REACT

what I try to do is to have the same display as this picture :
So in my menu the plant type (Type of plant1) is displayed above a gray bar and when you click on the down chevron then you can see all the plants name, related to this type, with checkboxes on left, by default there will be all checked. And the blue rectangle indicates the number of plants that have been selected.
How can I do that, which package can help me in REACT?
Here my plants.json :
{
"plants_type": [
{
"_id_type": "1",
"name_type": "Type of plant1",
"plants": [
{
"name": "Plant1.1",
"_id": "2"
},
{
"name": "Plant1.2",
"_id": "3"
}
]
},
{
"_id_type": "4",
"name_type": "Type of plant2",
"plants": [
{
"name": "Plant2.1",
"_id": "5"
},
{
"name": "Plant2.2",
"_id": "6"
}
]
}
]
}
You can create a dropdown list on your own like below. I have added the logic of selecting items to the data itself.
You can keep a component called Category to keep a single state of the parent menu item. Whether it's open or not. Then iterate over the plants as checkbox inputs to make them selectable.
I have used a simple initialize function to make all the items selected initially. This should work as you expect. Add a console log of selectionMenu to see how selected property changes while toggling items.
Move the inline styles to CSS classes to make the code more clear.
const data = { plants_type: [ { _id_type: "1", name_type: "Type of plant1", plants: [ { name: "Plant1.1", _id: "2" }, { name: "Plant1.2", _id: "3" } ] }, { _id_type: "4", name_type: "Type of plant2", plants: [ { name: "Plant2.1", _id: "5" }, { name: "Plant2.2", _id: "6" } ] } ] };
const Category = ({ _id_type, name_type, plants, changeSelection }) => {
const [toggleState, setToggleState] = React.useState(false);
return (
<div key={_id_type}>
<div
style={{
cursor: "pointer",
userSelect: "none",
display: "flex",
margin: "2px",
backgroundColor: "lightgray"
}}
onClick={() => setToggleState((prev) => !prev)}
>
<div>{name_type}</div>
<div
style={{
backgroundColor: "blue",
color: "white",
padding: "0px 10px",
marginLeft: "auto"
}}
>
{plants.filter(({ selected }) => selected).length}
</div>
</div>
<div style={{ marginLeft: "10px" }}>
{toggleState &&
plants.map(({ name, _id, selected }) => (
<div key={_id}>
<input
key={_id}
type="checkbox"
value={name}
checked={selected}
onChange={(e) => changeSelection(_id_type, _id, e.target.value)}
/>
{name}
</div>
))}
</div>
</div>
);
};
const App = () => {
const initializeSelectionMenu = (data) => {
return data.map((item) => {
return {
...item,
plants: item.plants.map((plant) => ({ ...plant, selected: true }))
};
});
};
const [selectionMenu, setSelectionMenu] = React.useState(
initializeSelectionMenu(data.plants_type)
);
console.log(selectionMenu);
const changeSelection = (catId, itemId, value) => {
setSelectionMenu((prevSelectionMenu) =>
prevSelectionMenu.map((item) => {
if (item._id_type === catId) {
return {
...item,
plants: item.plants.map((plant) => {
if (plant._id === itemId) {
return { ...plant, selected: !plant.selected };
}
return plant;
})
};
}
return item;
})
);
};
return (
<div>
{selectionMenu.map((item) => (
<Category
{...item}
changeSelection={changeSelection}
key={item._id_type}
/>
))}
</div>
);
}
ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div class='react'></div>

React Flow: How to remove an element?

I'm having issues with deleting elements in react-flow via a button.
I can delete elements fine using Backspace but the button only works for the first delete and after that it brings back the deleted node.
New to using react-flow and can't put my finger on the problem here. Is the state not getting changed some how?
Below is the code I use for react flow
CodeSandbox here.
import React, { useState, useCallback, useEffect } from "react";
import ReactFlow, {
removeElements,
addEdge,
Background,
} from "react-flow-renderer";
const onLoad = (reactFlowInstance) => {
reactFlowInstance.fitView();
console.log(reactFlowInstance.getElements());
};
const StackFlow = () => {
const initialElements = [
{
id: "0",
position: { x: 0, y: -100 },
sourcePosition: "bottom",
style: {
width: 100,
fontSize: 11,
color: "white",
background: "#6ec9c0",
},
data: {
label: (
<>
<button
className="w-md h-md border-2 border-black p-2"
onClick={() =>
remModelData("0")
}
>
Del
</button> <br /> <br />
<strong>Models</strong>
</>
),
},
},
{
id: "1",
position: { x: 100, y: 50 },
sourcePosition: "bottom",
targetPosition: "top",
data: {
label: (
<>
<button
className="w-md h-md border-2 border-black p-2"
onClick={() =>
remModelData("1")
}
>
Del
</button> <br /> <br />
Model: <strong>1</strong> <br />
ID: 1
</>
),
},
},
{
id: "2",
position: { x: 150, y: 250 },
sourcePosition: "bottom",
targetPosition: "top",
data: {
label: (
<>
<button
className="w-md h-md border-2 border-black p-2"
onClick={() =>
remModelData("2")
}
>
Del
</button> <br /> <br />
Model 1: <strong>subModel1</strong> <br />
ID: 2
</>
),
},
},
{
id: "3",
position: { x: 250, y: 250 },
sourcePosition: "bottom",
targetPosition: "top",
data: {
label: (
<>
<button
className="w-md h-md border-2 border-black p-2"
onClick={() =>
remModelData("3")
}
>
Del
</button> <br /> <br />
Model 1: <strong>subModel2</strong> <br />
ID: 3
</>
),
},
},
{
id: "0-1",
type: "step",
source: "0",
target: "1"
},
{
id: "1-2",
type: "step",
source: "1",
target: "2"
},
{
id: "1-3",
type: "step",
source: "1",
target: "3"
},
];
const [elements, setElements] = useState(initialElements);
const onElementsRemove = useCallback(
(elementsToRemove) =>
setElements((els) => removeElements(elementsToRemove, els)),
[]
);
const onConnect = (params) => setElements((els) => addEdge(params, els));
const [reactflowInstance, setReactflowInstance] = useState(null);
useEffect(() => {
if (reactflowInstance && elements.length > 0) {
reactflowInstance.fitView();
}
}, [reactflowInstance, elements.length]);
const remModelData = useCallback((id) => {
let arr = elements
var index = arr.indexOf(id);
if (index > -1) {
arr.splice(index, 1);
}
arr = arr.filter(function (obj) {
return obj.id !== id;
});
console.log(arr);
setElements(arr);
}, []);
return (
<ReactFlow
elements={elements}
onElementsRemove={onElementsRemove}
onConnect={onConnect}
onLoad={onLoad}
snapToGrid={true}
snapGrid={[15, 15]}
>
<Background color="#aaa" gap={16} />
</ReactFlow>
);
};
function Home() {
return (
<>
<div className="w-full mx-auto justify-center items-center flex">
<div
className="flex mt-10 flex-row items-center content-center justify-center max-h-5xl max-w-5xl py-2"
style={{ flex: 1, width: 1000, height: 800, borderWidth: 2 }}
>
<StackFlow />
</div>
</div>
</>
);
}
export default Home;
I was able to accomplish the end result I wanted by creating this function:
const deleteNode = (id) => {
setElements((els) => removeElements([elements[id]], els));
};
And passing this into the OnClick:
onClick={() => deleteNode(0)}
Changing 0 for what ever index the element you wish to remove from the array is
As from version 10 Elements has been replaced with Nodes and Edges.
I could achieve the following using the function below.
const deleteNodeById = (id) => {
flowInstance.setNodes((nds) => nds.filter((node) => node.id !== id))
}
And passing this function to my delete button (In my case mui delete icon)
<DeleteOutlined
onClick={() => deleteNodeById(data.id)}
style={{ color: '#FF0000' }}
/>
As of version 11.2 onwards, you can use
reactFlowInstance.deleteElements({ nodes: nodesToDelete, edges: edgesToDelete });
The advantage is that method will fire "onNodesDelete" & "onEdgesDelete" events so that you can reuse your handlers.
Recall that these events are also fired if you press the "backspace" button.

How can I setState from inside a render function in react

i know you are supposed to keep render functions pure but I have a special case where I need to pass some values and update the state inside a render function but i am getting the following error message:
Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state
Code:
import Carousel from 'react-elastic-carousel';
import top from '../images/top.png';
import ArrowRight from '../images/arrowRight.svg';
import React, {Component} from 'react';
class contentSlider extends Component {
state = {
disabled: '',
leftArrow: false,
rightArrow: true
}
sliderData = [
{
title: 'POLO',
typeOfcontent: 'Mens T-Shirt',
rrp: '£105',
ourPrice: '£55',
salePrice: '£45',
image: top
},
{
title: 'POLO',
typeOfcontent: 'Mens T-Shirt',
rrp: '£105',
ourPrice: '£55',
salePrice: '£45',
image: top
},
{
title: 'POLO',
typeOfcontent: 'Mens T-Shirt',
rrp: '£105',
ourPrice: '£55',
salePrice: '£45',
image: top
},
{
title: 'POLO',
typeOfcontent: 'Mens T-Shirt',
rrp: '£105',
ourPrice: '£55',
salePrice: '£45',
image: top
},
{
title: 'POLO',
typeOfcontent: 'Mens T-Shirt',
rrp: '£105',
ourPrice: '£55',
salePrice: '£45',
image: top
},
{
title: 'POLO',
typeOfcontent: 'Mens T-Shirt',
rrp: '£105',
ourPrice: '£55',
salePrice: '£45',
image: top
},
{
title: 'POLO',
typeOfcontent: 'Mens T-Shirt',
rrp: '£105',
ourPrice: '£55',
salePrice: '£45',
image: top
}
]
breakPoints = [
{ width: 2, itemsToShow: 2, itemsToScroll: 2 },
{ width: 550, itemsToShow: 3, itemsToScroll: 3},
{ width: 850, itemsToShow: 4, itemsToScroll: 3 },
{ width: 970, itemsToShow: 4, itemsToScroll: 3 },
{ width: 1150, itemsToShow: 5, itemsToScroll: 3 },
]
setDirection = (slideDirection) => {
switch(slideDirection) {
case "next":
this.carousel.slideNext();
let slideNext = document.getElementById('slider-move');
if(slideNext.classList.contains('test-right')) {
slideNext.classList.remove('test-right');
slideNext.classList.add('test-left');
}
break;
case "previous":
this.carousel.slidePrev();
let slidePrevious = document.getElementById('slider-move');
if(slidePrevious.classList.contains('test-left')) {
slidePrevious.classList.remove('test-left');
slidePrevious.classList.add('test-right');
}
break;
}
}
getAmountOfPages = (pages, activePage ) => {
console.log(activePage)
let firstItem = pages[0];
let [lastItem] = pages.slice(-1);
if(firstItem === activePage) {
this.setState({
leftArrow: false,
rightArrow: true,
})
} else if(lastItem === activePage) {
this.setState({
leftArrow: true,
rightArrow: false,
})
} else {
this.setState({
leftArrow: true,
rightArrow: false,
})
}
// get first item in the array and compare it to the active page
// get the last element in the array and compare it to the active page
}
render() {
return (
<div className="content-slider-wrapper">
<div className="content-slider-title">
<span>PRODUCTS OF THE WEEK</span>
</div>
<div className={`${this.sliderData.length === 5 ? 'd-xl-none' : ''} arrow-container`}>
<img onClick={() => this.setDirection("previous")} className="arrow-left" src={ArrowRight} />
<img onClick={() => this.setDirection("next")} src={ArrowRight} />
</div>
<div className={`${this.sliderData.length === 5 ? 'mt-xl-5' : ''} content-slider-container`}>
<div className="test-right" id="slider-move">
<Carousel
ref={ref => (this.carousel = ref)}
breakPoints={this.breakPoints}
disableArrowsOnEnd={true}
renderPagination={({ pages, activePage, onClick }) => {
this.getAmountOfPages(pages, activePage);
return (
<div className={`${this.sliderData.length === 5 ? 'd-xl-none' : ''} black-slider-container`}>
{pages.map(page => {
const isActivePage = activePage === page
return (
<div className={isActivePage ? 'black-slider' : 'blank-slider'}
key={page}
onClick={() => onClick(page)}
active={isActivePage}
/>
)
})}
</div>
)
}}
>
{this.sliderData.map((item, index) => (
<div key={index} className="carousel-item-container">
<div className="carousel-image-container">
<img src={top} />
</div>
<div className="carousel-text-container">
<ul>
<li className="carousel-text-container-title">{item.title}</li>
<li className="carousel-text-container-text">{item.typeOfProduct}</li>
<li className="carousel-text-container-text line-through">RRP {item.rrp}</li>
<li className="carousel-text-container-text line-through">Our Price: {item.ourPrice}</li>
<li className="carousel-text-container-text">Sale Price: {item.salePrice}</li>
</ul>
</div>
</div>
))}
</Carousel>
</div>
</div>
</div>
)
}
}
export default contentSlider;
I need to call this function
this.getAmountOfPages(pages, activePage);
which is inside my render to update the state when needed however it dont work any ideas....
I am Brazilian and therefore I speak Portuguese, but I will use the translator to try to help you.
By calling this function the way you are trying to do it, you would fall into an infinite loop. Look:
You update the state inside the render () method;
The render () method is called due to the state update;
The render method updates the state;
all over again
I've already used this library, so I'll show you how I did it:
<Carousel
itemsToShow={7}
renderArrow={renderArrow}
enableMouseSwipe={false}
pagination={false}
breakPoints={[
{
width: 450,
itemsToShow: 3,
enableSwipe: true,
enableMouseSwipe: true,
itemsToScroll: 3,
renderArrow: () => <div />,
},
{
width: 500,
itemsToShow: 3,
},
{ width: 620, itemsToShow: 4 },
{ width: 720, itemsToShow: 5 },
{ width: 850, itemsToShow: 6 },
{ width: 1150, itemsToShow: 7 },
]}
>
{products &&
products.map((p) => (
<div key={p.docId} className="products-prod">
<img
onClick={() => addProductCart(p)}
alt={p.descricao}
src={`data:image/png;base64,${p.thumbnail}`}
className={`shadow2 ${
!cart.includes(p.docId) && adding.includes(p.docId)
? `pulse`
: ``
}`}
/>
<section onClick={() => addProductCart(p)}>
{cart.includes(p.docId) ? (
<img src={ICONS.shoppingFilled} />
) : (
<img src={ICONS.shoppingOutlined} />
)}
</section>
<article>
<h5>R$</h5>
{p.preco && formatCash(p.preco, true)}
</article>
</div>
))}
</Carousel>
And:
const renderArrow = ({ onClick, type, isEdge: noMoreItems }) => (
<Button
onClick={onClick}
style={{
margin: "auto 0",
borderRadius: "100px",
background: COLORS.primary,
borderColor: COLORS.primary,
width: "30px",
minWidth: "30px",
height: "30px",
minHeight: "30px",
padding: 0,
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
type="primary"
>
{type === consts.PREV ? (
<CaretLeftOutlined style={{ margin: "2px 2px 0 0" }} />
) : (
<CaretRightOutlined style={{ margin: "2px 0 0 2px" }} />
)}
</Button>
);

Add/Remove React Grid Layout Example: TypeError: "exports" is read-only

I am having difficulties getting this code example from the ReactGridLayout library to work. I think I have solved some of the issues with pathing for css files, but I am not sure what to make of this error.
Link to functional exmaple:
https://strml.github.io/react-grid-layout/examples/6-dynamic-add-remove.html
import React from "react";
import { WidthProvider, Responsive } from "react-grid-layout";
import _ from "lodash";
const ResponsiveReactGridLayout = WidthProvider(Responsive);
/**
* This layout demonstrates how to use a grid with a dynamic number of elements.
*/
class AddRemoveLayout extends React.PureComponent {
static defaultProps = {
className: "layout",
cols: { lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 },
rowHeight: 100
};
constructor(props) {
super(props);
this.state = {
items: [0, 1, 2, 3, 4].map(function(i, key, list) {
return {
i: i.toString(),
x: i * 2,
y: 0,
w: 2,
h: 2,
add: i === (list.length - 1).toString()
};
}),
newCounter: 0
};
this.onAddItem = this.onAddItem.bind(this);
this.onBreakpointChange = this.onBreakpointChange.bind(this);
}
createElement(el) {
const removeStyle = {
position: "absolute",
right: "2px",
top: 0,
cursor: "pointer"
};
const i = el.add ? "+" : el.i;
return (
<div key={i} data-grid={el}>
{el.add ? (
<span
className="add text"
onClick={this.onAddItem}
title="You can add an item by clicking here, too."
>
Add +
</span>
) : (
<span className="text">{i}</span>
)}
<span
className="remove"
style={removeStyle}
onClick={this.onRemoveItem.bind(this, i)}
>
x
</span>
</div>
);
}
onAddItem() {
/*eslint no-console: 0*/
console.log("adding", "n" + this.state.newCounter);
this.setState({
// Add a new item. It must have a unique key!
items: this.state.items.concat({
i: "n" + this.state.newCounter,
x: (this.state.items.length * 2) % (this.state.cols || 12),
y: Infinity, // puts it at the bottom
w: 2,
h: 2
}),
// Increment the counter to ensure key is always unique.
newCounter: this.state.newCounter + 1
});
}
// We're using the cols coming back from this to calculate where to add new items.
onBreakpointChange(breakpoint, cols) {
this.setState({
breakpoint: breakpoint,
cols: cols
});
}
onLayoutChange(layout) {
this.props.onLayoutChange(layout);
this.setState({ layout: layout });
}
onRemoveItem(i) {
console.log("removing", i);
this.setState({ items: _.reject(this.state.items, { i: i }) });
}
render() {
return (
<div>
<button onClick={this.onAddItem}>Add Item</button>
<ResponsiveReactGridLayout
onLayoutChange={this.onLayoutChange}
onBreakpointChange={this.onBreakpointChange}
{...this.props}
>
{_.map(this.state.items, el => this.createElement(el))}
</ResponsiveReactGridLayout>
</div>
);
}
}
module.exports = AddRemoveLayout;
if (require.main === module) {
require("../test-hook.jsx")(module.exports);
}
As per
https://github.com/webpack/webpack/issues/3997
and
https://github.com/webpack/webpack/releases/tag/v2.2.0-rc.5
module.exports is read-only and undefined
We need to use export default AddRemoveLayout

Categories