Reading data from the server is an important part of an application but when we want to add interactivity we need to be able to also mutate it.
Interact gives us the power to perform CRUD operations on our resources defined by our containers by passing a prop interact
to the wrapped component.
The interact
prop exposes a fluent API where resources can be mutated and read using CRUD operations (read, update, create, remove, list).
In the below example a new Todo is created when we click the Add Todo button.
import React, { Component } from 'react'
import { createContainer } from 'react-interact'
class TodoList extends Component {
render() {
return (
<div>
{this.props.todos.map(todo => `${todo.title}, `)}
<a onClick={this.handleAddTodo.bind(this)}>Add Todo</a>
</div>
)
},
handleAddTodo() {
this.props.interact.todos.create({
title: 'New Todo',
})
},
}
export default createContainer(TodoList, {
todos: '/api/v1/todos',
})
In this example we update/toggle the done
property of the clicked Todo.
import React, { Component } from 'react'
import { createContainer } from 'react-interact'
class TodoList extends Component {
render() {
return (
<ul>
{this.props.todos.map(todo => {
const onClick = this.handleUpdateTodo.bind(this, {
id: todo.id,
payload: {
done: !todo.done,
},
})
return (
<li key={todo.id}>
<a onClick={onClick}>{todo.title}</a>
</li>
)
})}
</ul>
)
},
handleUpdateTodo({id, payload}) {
this.props.interact.todos({id}).update(payload)
},
}
export default createContainer(TodoList, {
todos: '/api/v1/todos',
})
This example shows how to remove a Todo when we click it.
import React, { Component } from 'react'
import { createContainer } from 'react-interact'
class TodoList extends Component {
render() {
return (
<ul>
{this.props.todos.map(todo => {
const onClick = this.handleRemoveTodo.bind(this, todo.id)
return (
<li key={todo.id}>
<a onClick={onClick}>{todo.title}</a>
</li>
)
})}
</ul>
)
},
handleRemoveTodo(id) {
this.props.interact.todos({id}).remove()
},
}
export default createContainer(TodoList, {
todos: '/api/v1/todos',
})
This example shows an example of typeahead functionality using the list
method.
import React, { Component } from 'react'
import { createContainer } from 'react-interact'
class TodoList extends Component {
render() {
return (
<div>
<input onChange={this.handleSearch.bind(this)} />
<ul>
{this.state.todos.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
</div>
)
},
handleSearch(event) {
this.props.interact
.todos({query: {search: event.target.value}})
.list((response, collection, done) => {
this.setState({todos: response.body})
done()
})
},
}
export default createContainer(TodoList, {
todos: '/api/v1/todos',
})
The list
method will not update the collection that gets passed as a prop to the wrapped component. If you wish to list and update the collection at the same time use the fetch
method instead.
What happens when mutating one object has an effect on other items in the collection? If for example we change the position of an item in a list, and on the backend we also update the positions of other items in the list - these other changes would need to be synced with the frontend.
One way of doing this is sending another network request that retrieves the updated list of items. There are other ways but we will look at this way for illustration purposes.
import React, { Component } from 'react'
import { createContainer } from 'react-interact'
class TodoList extends Component {
render() {
return (
<ul>
{
this.props.todos
.sort((a, b) => a.position - b.position)
.map(todo => {
const onClick = this.handleMoveTodoToTop.bind(this, todo)
return (
<li key={todo.id}>
<a onClick={onClick}>{todo.title}</a>
</li>
)
})
}
</ul>
)
},
handleMoveTodoToTop(todo) {
this.props.interact.todos({id}).update(
{position: 0},
(response, collection, updateCollection) => {
this.props.interact.todos.fetch()
}
)
},
}
export default createContainer(TodoList, {
todos: '/api/v1/todos',
})
Query strings can also be added to request urls by specifying a query property on the object that is passed to the method that selects the item in the resource. Each key-value pair is mapped to the uri query string using qs behind the scenes.
import React, { Component } from 'react'
import { createContainer } from 'react-interact'
class TodoList extends Component {
render() {
return (
<div>
<input onChange={this.handleSearch.bind(this)} />
<ul>
{
this.props.todos.map(todo => (
<li key={todo.id}>{todo.title}</li>
))
}
</ul>
</div>
)
},
handleSearch(event) {
this.props.interact.todos({id, query: {search: event.target.value}}).fetch()
},
}
export default createContainer(TodoList, {
todos: '/api/v1/todos',
})