Creating a Todos application is all the buzz now days. Getting with the times requires learning different ways to do things. We will code some React components that will use Google Material Design to quickly get some awesome looking interfaces.

The entire code base is located at:

https://gitlab.com/nthchildconsulting/material-ui-todos

We will be using the following technologies:

  • Create React App
  • Material UI
  • Redux

React App

index.js

import React from 'react';
import App from './containers/App/App';
import { render } from 'react-dom';
import registerServiceWorker from './registerServiceWorker';
import './index.css';
import { Provider } from 'react-redux';
import store from './redux/store';
const rootElement = document.getElementById('root');
render(
<Provider store={store}>
<App />
</Provider>,
rootElement
);
registerServiceWorker();

In this file we import our main App container and our Redux store. We then inject the store in a Provider component so that all the child components will have access to the Redux store.

We will focus on React components in this article. In the next article we will focus on the Redux store.

containers/App/App.js

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Footer from '../../containers/Footer/Footer';
import AddTodo from '../../containers/AddTodo/AddTodo';
import VisibilityTodoList from '../../containers/VisibilityTodoList/VisibilityTodoList';
import { withStyles } from '@material-ui/core/styles';
import Paper from '@material-ui/core/Paper';
import Grid from '@material-ui/core/Grid';
const styles = theme => ({
root: {
flexGrow: 1,
backgroundColor: theme.palette.background.paper,
},
paper: {
padding: theme.spacing.unit * 6,
[theme.breakpoints.down('xs')]: {
padding: theme.spacing.unit * 2,
},
textAlign: 'center',
color: theme.palette.text.secondary,
},
});
class App extends Component {
render() {
const { classes } = this.props;
return (
<div className={classes.root}>
<Grid container spacing={24} justify="center">
<Grid item xs={12} sm={10} md={8}>
<Paper className={classes.paper}>
<AddTodo />
<VisibilityTodoList />
</Paper>
</Grid>
</Grid>
<Footer />
</div>
);
}
}
App.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(App);

In the main App component we will start with some Material UI grids. We use the grid system for consistency between layouts. You can read more about grids here:

https://material-ui.com/layout/grid/

First create a Grid container then next the children under the container with Grid item

Our main app grid will just contain one item that will include add todo, and todo list components.

containers/AddTodo/AddTodo.js

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { addTodo } from '../../redux/actions/TodosActions';
import Input from '@material-ui/core/Input';
import Button from '@material-ui/core/Button';
import AddIcon from '@material-ui/icons/Add';
import { withStyles } from '@material-ui/core/styles';
import Grid from '@material-ui/core/Grid';
const styles = theme => ({
button: {
[theme.breakpoints.down('xs')]: {
width: '35px',
height: '35px',
},
},
});
class AddTodo extends Component {
state = {
todo: '',
};
handleChange = event => {
this.setState({ todo: event.target.value });
};
handleClick = () => {
if (!this.state.todo.trim()) {
return;
}
this.props.dispatch(addTodo(this.state.todo));
this.setState({ todo: '' });
};
render() {
const { classes } = this.props;
return (
<div>
<Grid container spacing={24} justify="center">
<Grid item xs={10} sm={10}>
<Input
fullWidth
placeholder="What will you do next?"
value={this.state.todo}
onChange={this.handleChange}
onKeyPress={e => {
if (e.key === 'Enter') {
e.preventDefault();
this.handleClick();
}
}}
/>
</Grid>
<Grid item xs={2}>
<Button
variant="fab"
color="primary"
aria-label="add"
className={classes.button}
onClick={this.handleClick}
>
<AddIcon />
</Button>
</Grid>
</Grid>
</div>
);
}
}
AddTodo.propTypes = {
classes: PropTypes.object.isRequired,
};
export default connect()(withStyles(styles)(AddTodo));

In the AddTodo component we will have another grid container with two grid items. The great thing about Material UI is that you can use its breakpoints to use for different width devices.

xs={10} // For smaller devices only span 10 columns
sm={8} // For small devices and up only span 8 columns

Then using the state through React, once the button is clicked, it will dispatch a Redux action to add the todo to the store.

components/TodoList/TodoList.js

import React from 'react';
import PropTypes from 'prop-types';
import Todo from '../Todo/Todo';
import List from '@material-ui/core/List';
const TodoList = ({ todos, toggleTodo }) => (
<List>
{todos.map(todo => (
<Todo key={todo.id} {...todo} onClick={() => toggleTodo(todo.id)} />
))}
</List>
);
TodoList.propTypes = {
todos: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number.isRequired,
completed: PropTypes.bool.isRequired,
text: PropTypes.string.isRequired,
}).isRequired
).isRequired,
toggleTodo: PropTypes.func.isRequired,
};
export default TodoList;

In the TodoList we bring in the List component which has many customizable properties. You can read more here:

https://material-ui.com/demos/lists/

components/Todo/Todo.js

import React from 'react';
import PropTypes from 'prop-types';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
const Todo = ({ onClick, completed, text }) => (
<ListItem
divider
onClick={onClick}
style={{
textDecoration: completed ? 'line-through' : 'none',
}}
>
<ListItemText primary={text} />
</ListItem>
);
Todo.propTypes = {
onClick: PropTypes.func.isRequired,
completed: PropTypes.bool.isRequired,
text: PropTypes.string.isRequired,
};
export default Todo;

In the Todo component we use the ListItem and ListItemTextcomponents. This makes it easy for material ui to line up components.

Conclusion

With just a few Material UI components you can quickly make an expressive UI for use on modern browsers and mobile devices.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store