How to map inside the TransitionMotion wrapper? - javascript

I'm trying to implement react-motion's TransitionMotion wrapper and made it to the home stretch but there's one more issue. In this example the interpolated -array consists of two elements (because chartConfigs.length is currently 2) and I've nested another map inside the first one. Everything else works fine except I obviously get two rows when I only want one. How to go around this in a clean way?
const getStyles = () => {
return chartConfigs.map(datum => ({
data: datum,
style: {
opacity: spring(1, { stiffness: 30})
},
key: datum.name
}))
}
const getDefaultStyles = () => {
return chartConfigs.map(datum =>({
data: datum,
style: {
opacity: 0
},
key: datum.name
}))
}
return (
<TransitionMotion
defaultStyles={getDefaultStyles()}
styles={getStyles()}
>
{(interpolated) => (
<div>
{interpolated.map((config) => (
<div key={config.key} style={{ ...config.style }}>
<div className='row' style={{ paddingTop: "30px" }}>
{chartConfigs.length > 1 &&
chartConfigs.map((chartConfig, i) => {
return (
<div
className={`col-lg-${columnsCount}`}
key={"chart-toggler" + i}
>
<div className='card m-b-30'>
<h4 className='card-title font-16 mt-0'>
{chartConfig.name}
</h4>
</div>
</div>
)
})}
</div>
</div>
))}
</div>
)}
</TransitionMotion>
)
EDIT:
Here's the new version of my solution but with the struggle of displaying elements next to each other on the row:
<div className='row' style={{ paddingTop: "30px" }}>
{chartConfigs.length > 1 ?
<TransitionMotion
defaultStyles={getDefaultStyles()}
styles={getStyles()}
willEnter={willEnter}
willLeave={willLeave}
>
{interpolated => (
<div id='container' style={{width: '100%', display: 'inline-block'}} >
{interpolated.map((config, i) => (
<div key={config.key} style={{ ...config.style }}>
{(selected = config.data.name === currentChartName)}
<div
className={`col-lg-${columnsCount}`}
key={"chart-toggler" + i}
>
<div
className={
selected
? "card m-b-30 text-white bg-primary"
: "card m-b-30"
}
style={{
width: '100%',
height: "calc(100% - 30px)",
}}
onClick={() => setCurrentChartName(config.data.name)}
>
<div className='card-body'>
<h4 className='card-title font-16 mt-0'>
{config.data.name}
</h4>
</div>
</div>
</div>
</div>
))}
</div>
)}
</TransitionMotion>
: null }
</div>
Additionally, I'm having trouble understanding how to use TransitionMotion when component unmounts. So basically the fade out effect when I render a different component on the page. Can I use the willLeave() function for this? Currently this is what it looks like but I don't know how to take it further:
const willLeave = () => ({
opacity: spring(0)
})
Thanks for your time and help!

TransitionMotion intentionally gives you more than the number of rows you’re currently rendering, since it remembers all the rows that are in the process of animating out.
So it depends on what you’re trying to achieve here. My hunch is that you’re probably misused chatConfigs in the inner level. You should be accessing config.data.name instead of chartConfig.name, no? You know what I mean?

Related

cleaning setstate in react

I have a function called getLines which tries to find out when text is going to wrap in another line it is async and it is so time-consuming
and I have an API that gives a string with a given length (query)
import { useState, useEffect } from "react";
import getLines from "./getline"; //this function take time according to input
export default function App() {
const [text, setText] = useState<string>("");
const [arr, setArr] = useState<string[] | "error" | undefined>([]);
const [time, setTime] = useState<100 | 200 | 300 | "un">("un");
const [pending, setPending] = useState(false);
useEffect(() => {
if (time !== "un") {
(async () => {
setPending(true);
let res = await fetch(
`https://random-word-genrator.vercel.app/words?w=${time}`
);
let { response } = await res.json();
let a = await getLines(800, response, "1.2rem", "3px");
setArr(a);
setText(response);
setPending(false);
})();
}
}, [time]);
return (
<div>
<div style={{ display: "flex", gap: "1rem" }}>
<div
onClick={() => {
setTime(100);
}}
>
100
</div>
<div
onClick={() => {
setTime(200);
}}
>
200
</div>
<div
onClick={() => {
setTime(300);
}}
>
300
</div>
</div>
{pending ? (
<div>loading</div>
) : (
<div>
<div>value:={text}</div>
<div>array:={JSON.stringify(arr)}</div>
<div>length:={text.split(" ").length}</div>
</div>
)}
</div>
);
}
video of problem
you can see in video that when I click 100 it give me 100 words when I click to 200 it give me 200 words but when I click multiple button it gives me different length then what I last clicked
can anyone tell me how can i remove this problem?
sanboxLink:- sendbox link
**recreate error by quickly clicking 300 and 100 with this order 300--quickly-->100
Clicking multiple times invokes multiple operations. Since the operations are asynchronous, they can complete in any order.
If you don't want the user to be able to invoke multiple simultaneous operations, prevent/ignore the click when an operation is still pending. The pending state value you already have seems like a reasonable way to check that.
For example, you can display "loading" instead of the "buttons" (well, clickable divs):
return (
<div>
{pending ? (
<div>loading</div>
) : (
<div style={{ display: "flex", gap: "1rem" }}>
<div onClick={() => { setTime(100); }} >
100
</div>
<div onClick={() => { setTime(200); }} >
200
</div>
<div onClick={() => { setTime(300); }} >
300
</div>
</div>
<div>
<div>value:={text}</div>
<div>array:={JSON.stringify(arr)}</div>
<div>length:={text.split(" ").length}</div>
</div>
)}
</div>
);
Alternatively, you could keep the UI but just ignore the click:
<div onClick={() => {
if (!pending) {
setTime(100);
}
}} >
100
</div>

On toggle every array is getting open in react js

export const MobileFAQ = ({ faqs = [], defaultOpen = false }) => {
const { isOn: open, toggle } = useToggle(defaultOpen);
const ToggleIcon = open ? MinusIcon24Gray : PlusIcon24Gray;
return (
<div className="p16">
<h4 className="section-text-5 mb16">Frequently asked questions</h4>
{faqs.map((u, index) => (
<>
<div className="faq-item" onClick={toggle}>
<span className="c-black-3 text-medium">{u.question}</span>
<div className="faq-toggle-icon-mobile"></div>
</div>
<Collapse in={open}>
<div className="faq-answer c-black-3 text-4">{u.ans}</div>
</Collapse>
{index !== faqs.length - 1 && (
<div
style={{ height: 1, width: '100%', backgroundColor: '#e6e6e6' }}
className="mt12 mb16"
/>
)}
</>
))}
</div>
);
};
I have created faq array which is showing question and answer on toggle but it get open every index which should be coming as index wise

How to toggle class when user selects a list item coming from api in react.s?

I'm working on an application where I render the DB results by a loop through using the map method.
When the user clicks on each item, first it will add those id's into an array and toggle a CSS class to change the border color of a particular selected item, when the item is selected again, the border will disappear, same goes for all items rendered in the UI. Adding the id's into an array is working fine.
I'm facing a problem toggling CSS style to change the border color when click on each item.
Here is my code to render items on DOM
const [selectedItems, setSelectedItems] = React.useState([])
const onClickHandler = (id) => {
if (selectedItems.indexOf(id) > -1) {
let updatedItems = selectedItems.filter(itemID => itemID !== id)
console.log(updatedItems);
setSelectedItems(updatedItems)
} else {
setSelectedItems(prevState => [...prevState, id] )
}
setOpen(true);
}
{
courses.length > 0 && courses.map(course => {
return (
<>
<CourseItem course={course} onClickHandler={onClickHandler} itemSelect={itemSelect} />
</>
)
})
}
function CourseItem({ course, onClickHandler }) {
const classes = useStyles();
return (
<>
<Col style={{ marginTop: 10 }}>
<Paper elevation={0} className={classes.card}>
<div className={classes.cardTop} style={{ backgroundImage: `url('${course.thumbnail}')`, backgroundSize: 'cover', backgroundPosition: 'center' }}>
<div style={{ padding: '8px 18px', display: 'flex', justifyContent : 'space-between' }}>
<CheckCircleIcon onClick={() => onClickHandler(course._id)} />
{/* <Typography component={"label"} variant="caption"> { course.level }</Typography> */}
<Chip label={course.level} style={{ color: '#000', backgroundColor: '#f9f9f9', fontFamily: 'regular1', position: 'relative', zIndex: 0}} variant="outlined" />
</div>
</div>
<div className={classes.cardBottom}>
<Typography> { course.moduleName }</Typography>
<Typography variant="subtitle2" component={"span"}> { course.description }</Typography>
</div>
</Paper>
</Col>
</>
)
}
You can use tow different class for two different condition. Then use clsx to conditionally apply class.
clsx link -> npm clsx

multiple iteration is done for creating the generic filters

I am trying to develop a generic filter component which can have many fields to filter on like color,
size, price range etc and each field might have different types of elements like color may have
checkboxes, radio button and price range might have input element, dropdown etc. To support such varied
cases, I tried to go with this pattern but here I have to iterate the same things multiple times.
I am not sure of this data structure. If anyone has suggestion please help me to improve this code but
the main problem here is "multiple iteration". How can i improve this code?
const filterParams = {
field: {
id : 1, label : 'Field', content: <FieldFilter />
},
employee: {
id : 1, label : 'Employee', content: <Employee />
}
}
<Filter filterParams={filterParams} activeFilterParam="field" />
const Filter = ({ filterParams, activeFilterParam }) => {
const [show, setShow]=useState(false)
return (
<>
<Button secondary icon={filter} onClick={() => setShow(!show)}>Filter</Button>
{show && (
<Card style={{ marginTop: 10 }}>
<Card.Content>
<Tabs activeTab={activeFilterParam}>
<Tabs.List
render={() => {
return (
Object.keys(filterParams).map(filterParam => {
return (
<Tabs.Tab key={filterParam} id={filterParam}>{filterParams[filterParam].label}</Tabs.Tab>
)
}))
}} />
<Tabs.Panels>
{Object.keys(filterParams).map(filterParam => {
return (
<Tabs.Panel key={filterParam} panelId={filterParam}>{filterParams[filterParam].content}</Tabs.Panel>
)
})}
</Tabs.Panels>
</Tabs>
</Card.Content>
<Card.Footer>
<Button>
<Button.Content style={{ marginRight: 10 }}>Save</Button.Content>
<Button.Content secondary onClick={()=>setShow(!show)}>Cancel</Button.Content>
</Button>
</Card.Footer>
</Card>
)}
</>
)
}
If you're not liking the multiple calls to Object.keys(filterParams).map, you could move the loop to the top of the component function. Something like the below might work:
const Filter = ({ filterParams, activeFilterParam }) => {
const [show, setShow]=useState(false)
const {tabs, panels} = Object.keys(filterParams)
.reduce((acc, filterParam) => {
acc.tabs.push(
<Tabs.Tab key={filterParam} id={filterParam}>{filterParams[filterParam].label}</Tabs.Tab>
);
acc.panels.push(
<Tabs.Panel key={filterParam} panelId={filterParam}>{filterParams[filterParam].content}</Tabs.Panel>
);
return acc;
}, { tabs: [], panels: [] });
return (
...
<Card style={{ marginTop: 10 }}>
<Card.Content>
<Tabs activeTab={activeFilterParam}>
<Tabs.List render={() => tabs} />
<Tabs.Panels>
{panels}
</Tabs.Panels>
</Tabs>
...
</Card>
...
)
}
Note that I haven't run this - it likely won't be quite right, but should give the general idea...

Can I set onClickevents that are not yet defined?

I have a component that looks like this:
const ProductCarousel = React.createClass({
componentDidMount: function () {
this.flky = new Flickity('.carousel', flickityOptions)
//here
},
render: function () {
const item = this.props.item
return (
<div>
<div className='carousel'>
{item.get('images').map((url, i) => (
<img key={i.toString()} src={stripUrl(url)} width={520} />
))}
</div>
<div style={{marginTop: '20px', display: 'flex'}}>
{item.get('images').map((url, index) =>
<div style={{flexGrow: 1, margin: '0 1em 0 1em'}} className='hidden-xs' key={url}>
<Thumbnail src={stripUrl(url)} />
</div>
)}
</div>
</div>
)
}
})
the place where it says "here", I would like to define funcitons for <Thumbnail onClick />. Theese functions are methods for this.flky, so I can't create them before componentDidMount, but I would like to set them in the thumbnail and pass the thumbnails index to them. Is there something like a promise I could use?
If you put it in a function, it won't get executed until it gets clicked, and the methods should exist by then:
<Thumbnail src={stripUrl(url)} onClick={()=>this.flky.someMethod(index)} />

Categories

Resources