2

I've built a simple add-your-todos system in React using Semantic-UI-React. It currently looks like this:

before-transition-attempt

Problem description, and my attempt

When the user clicks on the red bin icon, I'd like to delete the Table.Row using a Fadeout Transition. It currently deletes the row, but an animation here would make the user experience more enjoyable. I've visited the docs and tried to implement the solution, using a Transition.Group component.

...but it didn't work well

After trying to solve this problem on my own, I got some unexpected behaviours. First of all, rows don't fadeout. Furthermore, all Table.Cell kind of "blend" into one single cell, which is annoying. See the (gruesome) result:

after-transition-attempt

The Todo component (before)

Each Todo row is being dynamically added on the Table using a Todo component, which, before implementing Transition.Group, looked like this:

const React = require('react');
const moment = require('moment');
import { Table, Checkbox, Icon, Popup, Grid, Button } from 'semantic-ui-react';

class Todo extends React.Component {
    constructor(props) {
        super(props);
    }

    render() {
        const { id, text, completed, createdAt, completedAt } = this.props;
        const renderDate = (date) => {
            const timestamp = date;
            if (timestamp)
                return `${moment.unix(timestamp).format('MMM Do YYYY @ h:mm a')}`;
            return '';
        }

        const renderPopup = () => {
            if (completedAt) {
                return (
                    <Popup  trigger={<Icon name="calendar check" size="large"/>} header={'Completed at'} content={renderDate(completedAt)}/>
                );
            } else {
                return (
                    ''
                );
            }
        }

        return (
            <Table.Row>
                <Table.Cell>
                    <Grid columns="equal">
                        <Grid.Column width={3}>
                            <Checkbox toggle
                            defaultChecked={completed}
                            onClick={() => this.props.onToggle(id)} />
                        </Grid.Column>
                        <Grid.Column textAlign="left">
                            {renderPopup()}
                        </Grid.Column>
                    </Grid>
                </Table.Cell>
                <Table.Cell>{text}</Table.Cell>
                <Table.Cell>{renderDate(createdAt)}</Table.Cell>
                <Table.Cell textAlign="right">
                    <Button basic color="red" icon="trash"
                            onClick={() => {
                                this.props.onRemoveTodo(id);
                                this.handleFadeoutItem();
                                }}/>
                </Table.Cell>
            </Table.Row>
        );
    }
}

module.exports = Todo;

The component (after)

This is how it looks now (which is obviously wrong!):

const React = require('react');
const moment = require('moment');
import { Table, Checkbox, Icon, Popup, Grid, Button, Transition } from 'semantic-ui-react';

class Todo extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            visible: true
        };

        this.handleFadeoutItem = this.handleFadeoutItem.bind(this);
    }

    handleFadeoutItem () {
        this.setState({
            visible: false
        });
    }

    render() {
        const { visible } = this.state;
        const { id, text, completed, createdAt, completedAt } = this.props;
        const renderDate = (date) => {
            const timestamp = date;
            if (timestamp)
                return `${moment.unix(timestamp).format('MMM Do YYYY @ h:mm a')}`;
            return '';
        }

        const renderPopup = () => {
            if (completedAt) {
                return (
                    <Popup  trigger={<Icon name="calendar check" size="large"/>} header={'Completed at'} content={renderDate(completedAt)}/>
                );
            } else {
                return (
                    ''
                );
            }
        }

        return (
            <Transition.Group as={Table.Row} visible={visible} animation="fade" duration={500}>
                <Table.Row>
                    <Table.Cell>
                        <Grid columns="equal">
                            <Grid.Column width={3}>
                                <Checkbox toggle
                                defaultChecked={completed}
                                onClick={() => this.props.onToggle(id)} />
                            </Grid.Column>
                            <Grid.Column textAlign="left">
                                {renderPopup()}
                            </Grid.Column>
                        </Grid>
                    </Table.Cell>
                    <Table.Cell>{text}</Table.Cell>
                    <Table.Cell>{renderDate(createdAt)}</Table.Cell>
                    <Table.Cell textAlign="right">
                        <Button basic color="red" icon="trash"
                                onClick={() => {
                                    this.props.onRemoveTodo(id);
                                    this.handleFadeoutItem();
                                    }}/>
                    </Table.Cell>
                </Table.Row>
            </Transition.Group>
        );
    }
}

module.exports = Todo;

Any help offered will be greatly appreciated!


EDIT

@Adrien's answer partially solved my problem. Now every cell is in its place, but the animation transition doesn't seem to play. Furthermore, the calendar icon next to my "completed" checkboxes (check the initial, unmodified version of the app) seem to disappear. Any idea why these two things happen? See:

still-not-quite-right

3 Answers 3

4

You will need to do two things.

First, override your CSS. The transition add a display: block !important on your <Table.Row>, you need to restore the old value.

.table tr.visible.transition {
  display: table-row !important;
}

Then, according to the docs, you need to create your Transition.Group not on the <Table.Row> but on its parent, so <Table.Body> in your case. Here is an example:

<Transition.Group
    as={Table.Body}
    duration={200}
>
    {items.map(item => (
        <Table.Row>
            <Table.Cell>id</Table.Cell>
            <Table.Cell>foo</Table.Cell>
            <Table.Cell>bar</Table.Cell>
        </Table.Row>
    ))}
</Transition.Group>

You can find a live example here to see the different steps.

Sign up to request clarification or add additional context in comments.

1 Comment

That partially solved my problem. However, the transition animation is not playing (even though it takes it time to animate), and the "calendar" icon next to my checkbox disappears all of a sudden. Check my updated answer!
2

Previous answers have already point you in right way. I've made an minimal example that shows how it works.

As MEnf said, Transition.Group works only on mount/unmount, in context of React this means that animation will start when component will be added as new child or will be removed.

Comments

1

The reason your animation is not working is because as per the documentation, Transition.Group will only work when mounting/unmounting a component. Remove the visible attribute from the Transition.Group tag. Change as={Table.Row} to as={Table.Body}. By unmounting/mounting I mean every time a new element is added to your Todo array it should either render a new todo row component to the page or it should remove it instead of hiding/showing it.

*edited for clarification

1 Comment

Thanks for the answer. Would you please elaborate a bit more on this: "...and have the component unmount". How do I unmount it? What "visible tag" do you mean? Thanks

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.