I am currently trying to implemented a draggable re-orderable list with Framer Motion. I am trying to reproduce the following behaviour:
I have an ingredientList
that contains the create_index
of the each of the ingredients created in my Formik form, which are accessible as values.ingredients
.
The main issue I am having is that the list items are not behaving smoothly as they do in the code example and I'm not sure what I am doing wrong
This is my parent component in which I set the ingredientList
const IngredientTab = ({values}: any) => {
const [ingredientList, setIngredientList] = useState(values.ingredients.map((ingredient) => ingredient.create_index))
return (
<div css={IngredientTabStyles}>
<FieldArray name="ingredients">
{({ insert, remove, push }) => (
<div>
<Reorder.Group axis="y" values={ingredientList} onReorder={setIngredientList}>
<AnimatePresence>
{ingredientList.map((ingredientUniqueValue, index) => {
return (
<Ingredient
key={index}
index={index}
uniqueValue={ingredientUniqueValue}
ingredients={values.ingredients}
order={`${ingredientList.indexOf(ingredientUniqueValue) + 1}`}
/>
)
})}
</AnimatePresence>
</Reorder.Group>
<button
type="button"
className="add-ingredients"
onClick={() => {
setIngredientList([...ingredientList, values.ingredients.length])
push({ name: '', additional_info: '', quantity: '', unit_id: '', create_index: values.ingredients.length})
}}
>
Add Ingredient
</button>
</div>
)}
</FieldArray>
</div>
)
}
export default IngredientTab
const IngredientTabStyles = css`
.ingredient-fields {
margin-bottom: 2rem;
background-color: white;
}
`
And this is the Item component:
const Ingredient = ({ uniqueValue, ingredients, order, ingredient}: any) => {
const y = useMotionValue(0);
const boxShadow = useRaisedShadow(y);
const ingredientIndex = ingredients.findIndex(ingredient => ingredient.create_index==uniqueValue)
return (
<Reorder.Item
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
key={uniqueValue}
value={uniqueValue}
style={{boxShadow,y}}
>
{ingredient}
<div className="ingredient-fields" css={IngredientStyles}>
<div className="order">
<h6>{order}</h6>
</div>
<div className="ingredient-name">
<Field
name={`ingredients.${ingredientIndex}.name`}
type='text'
placeholder="Ingredient"
/>
<Field
name={`ingredients.${ingredientIndex}.additional_info`}
type='text'
placeholder="Description"
/>
</div>
<Field
name={`ingredients.${ingredientIndex}.quantity`}
type='number'
placeholder="Quantity"
/>
</div>
</Reorder.Item>
)
}
export default Ingredient
const IngredientStyles = css`
display: flex;
margin-bottom: 2rem;
.order {
display: flex;
align-items: center;
background-color: ${theme.components.grey};
padding: 1rem 2rem;
margin-right: 2rem;
border-radius: 0.4rem;
}
.ingredient-name {
display: flex;
}
input {
padding-bottom: 1rem;
border: none;
border-bottom: 1px solid ${theme.colors.lightGrey};
font-family: 'Source Sans Pro', sans-serif;
font-weight: 300;
}
`
I tried to take screenshots of the behaviour that I currently have. If I try to drag the 'bonjour' to the first position, the 1st item 'hello' does not move downwards. Instead what happens onReorder, what you see in the second picture, the bonjour and hello abruptly switch and it looks as though I am dragging the 'hello' item
I had similar behavior and found that the key is of utmost importance.