Created
August 28, 2020 14:52
-
-
Save lelandrichardson/4cfbb7aab96e01803cc7b289c2303e45 to your computer and use it in GitHub Desktop.
Recoil vs Compose
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const textState = atom({ | |
key: 'textState', // unique ID (with respect to other atoms/selectors) | |
default: '', // default value (aka initial value) | |
}); | |
const charCountState = selector({ | |
key: 'charCountState', // unique ID (with respect to other atoms/selectors) | |
get: ({get}) => { | |
const text = get(textState); | |
return text.length; | |
}, | |
}); | |
const todoListState = atom({ | |
key: 'todoListState', | |
default: [], | |
}); | |
const filteredTodoListState = selector({ | |
key: 'filteredTodoListState', | |
get: ({get}) => { | |
const filter = get(todoListFilterState); | |
const list = get(todoListState); | |
switch (filter) { | |
case 'Show Completed': | |
return list.filter((item) => item.isComplete); | |
case 'Show Uncompleted': | |
return list.filter((item) => !item.isComplete); | |
default: | |
return list; | |
} | |
}, | |
}); | |
function Example() { | |
// read atom | |
const [text, setText] = useRecoilState(textState) | |
text | |
// write atom | |
setText("foo") | |
// read selector | |
const filteredTodoList = useRecoilValue(filteredTodoListState) | |
} | |
// Atom | |
const textState = atom({ | |
key: 'textState', // unique ID (with respect to other atoms/selectors) | |
default: '', // default value (aka initial value) | |
}); | |
function CharacterCounter() { | |
return ( | |
<div> | |
<TextInput /> | |
<CharacterCount /> | |
</div> | |
); | |
} | |
function TextInput() { | |
const [text, setText] = useRecoilState(textState); | |
const onChange = (event) => { | |
setText(event.target.value); | |
}; | |
return ( | |
<div> | |
<input type="text" value={text} onChange={onChange} /> | |
<br /> | |
Echo: {text} | |
</div> | |
); | |
} | |
// selector | |
const charCountState = selector({ | |
key: 'charCountState', // unique ID (with respect to other atoms/selectors) | |
get: ({get}) => { | |
const text = get(textState); | |
return text.length; | |
}, | |
}); | |
function CharacterCount() { | |
const count = useRecoilValue(charCountState); | |
return <>Character Count: {count}</>; | |
} | |
// Todo List | |
const todoListState = atom({ | |
key: 'todoListState', | |
default: [], | |
}); | |
function TodoList() { | |
const todoList = useRecoilValue(todoListState); | |
return ( | |
<> | |
{/* <TodoListStats /> */} | |
{/* <TodoListFilters /> */} | |
<TodoItemCreator /> | |
{todoList.map((todoItem) => ( | |
<TodoItem key={todoItem.id} item={todoItem} /> | |
))} | |
</> | |
); | |
} | |
function TodoItemCreator() { | |
const [inputValue, setInputValue] = useState(''); | |
const setTodoList = useSetRecoilState(todoListState); | |
const addItem = () => { | |
setTodoList((oldTodoList) => [ | |
...oldTodoList, | |
{ | |
id: getId(), | |
text: inputValue, | |
isComplete: false, | |
}, | |
]); | |
setInputValue(''); | |
}; | |
const onChange = ({target: {value}}) => { | |
setInputValue(value); | |
}; | |
return ( | |
<div> | |
<input type="text" value={inputValue} onChange={onChange} /> | |
<button onClick={addItem}>Add</button> | |
</div> | |
); | |
} | |
// utility for creating unique Id | |
let id = 0; | |
function getId() { | |
return id++; | |
} | |
function TodoItem({item}) { | |
const [todoList, setTodoList] = useRecoilState(todoListState); | |
const index = todoList.findIndex((listItem) => listItem === item); | |
const editItemText = ({target: {value}}) => { | |
const newList = replaceItemAtIndex(todoList, index, { | |
...item, | |
text: value, | |
}); | |
setTodoList(newList); | |
}; | |
const toggleItemCompletion = () => { | |
const newList = replaceItemAtIndex(todoList, index, { | |
...item, | |
isComplete: !item.isComplete, | |
}); | |
setTodoList(newList); | |
}; | |
const deleteItem = () => { | |
const newList = removeItemAtIndex(todoList, index); | |
setTodoList(newList); | |
}; | |
return ( | |
<div> | |
<input type="text" value={item.text} onChange={editItemText} /> | |
<input | |
type="checkbox" | |
checked={item.isComplete} | |
onChange={toggleItemCompletion} | |
/> | |
<button onClick={deleteItem}>X</button> | |
</div> | |
); | |
} | |
function replaceItemAtIndex(arr, index, newValue) { | |
return [...arr.slice(0, index), newValue, ...arr.slice(index + 1)]; | |
} | |
function removeItemAtIndex(arr, index) { | |
return [...arr.slice(0, index), ...arr.slice(index + 1)]; | |
} | |
// selectors: ToDoList | |
const todoListFilterState = atom({ | |
key: 'todoListFilterState', | |
default: 'Show All', | |
}); | |
const filteredTodoListState = selector({ | |
key: 'filteredTodoListState', | |
get: ({get}) => { | |
const filter = get(todoListFilterState); | |
const list = get(todoListState); | |
switch (filter) { | |
case 'Show Completed': | |
return list.filter((item) => item.isComplete); | |
case 'Show Uncompleted': | |
return list.filter((item) => !item.isComplete); | |
default: | |
return list; | |
} | |
}, | |
}); | |
function TodoList() { | |
// changed from todoListState to filteredTodoListState | |
const todoList = useRecoilValue(filteredTodoListState); | |
return ( | |
<> | |
<TodoListStats /> | |
<TodoListFilters /> | |
<TodoItemCreator /> | |
{todoList.map((todoItem) => ( | |
<TodoItem item={todoItem} key={todoItem.id} /> | |
))} | |
</> | |
); | |
} | |
function TodoListFilters() { | |
const [filter, setFilter] = useRecoilState(todoListFilterState); | |
const updateFilter = ({target: {value}}) => { | |
setFilter(value); | |
}; | |
return ( | |
<> | |
Filter: | |
<select value={filter} onChange={updateFilter}> | |
<option value="Show All">All</option> | |
<option value="Show Completed">Completed</option> | |
<option value="Show Uncompleted">Uncompleted</option> | |
</select> | |
</> | |
); | |
} | |
const todoListStatsState = selector({ | |
key: 'todoListStatsState', | |
get: ({get}) => { | |
const todoList = get(filteredTodoListState); | |
const totalNum = todoList.length; | |
const totalCompletedNum = todoList.filter((item) => item.isComplete).length; | |
const totalUncompletedNum = totalNum - totalCompletedNum; | |
const percentCompleted = totalNum === 0 ? 0 : totalCompletedNum / totalNum; | |
return { | |
totalNum, | |
totalCompletedNum, | |
totalUncompletedNum, | |
percentCompleted, | |
}; | |
}, | |
}); | |
function TodoListStats() { | |
const { | |
totalNum, | |
totalCompletedNum, | |
totalUncompletedNum, | |
percentCompleted, | |
} = useRecoilValue(todoListStatsState); | |
const formattedPercentCompleted = Math.round(percentCompleted * 100); | |
return ( | |
<ul> | |
<li>Total items: {totalNum}</li> | |
<li>Items completed: {totalCompletedNum}</li> | |
<li>Items not completed: {totalUncompletedNum}</li> | |
<li>Percent completed: {formattedPercentCompleted}</li> | |
</ul> | |
); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var text by mutableStateOf("") | |
val charCount: Int get() = text.length | |
val todoList = mutableStateListOf<Item>(emptyList()) | |
val filteredTodoList get() = when (todoListFilter) { | |
Filter.Completed -> todoList.filter { it.isComplete } | |
Filter.Uncompleted -> todoList.filter { !it.isComplete } | |
Filter.All -> todoList | |
} | |
@Composable fun Example() { | |
// read atom | |
text | |
// write atom | |
text = "foo" | |
// read selector | |
filteredTodoList | |
} | |
// Section 1: Atom | |
var text by mutableStateOf("") | |
@Composable fun CharacterCounter() { | |
TextInput() | |
CharacterCount() | |
} | |
@Composable fun TextInput() { | |
TextField(value = text, onValueChange = { text = it }) | |
Text("Echo $text") | |
} | |
// Section 2: selector | |
val charCount: Int get() = text.length | |
@Composable fun CharacterCount() { | |
Text("Character Count: $charCount") | |
} | |
// Section 3: Todo List Atom | |
data class Item(val id: Int, val text: String, val isComplete: Boolean) | |
val todoList = mutableStateListOf<Item>(emptyList()) | |
@Composable fun TodoList() { | |
/* <TodoListStats /> */ | |
/* <TodoListFilters /> */ | |
TodoItemCreator() | |
todoList.forEach { | |
key(it.id) { TodoItem(item=it) } | |
} | |
} | |
@Composable fun TodoItemCreator() { | |
var inputValue by remember { mutableStateOf("") } | |
TextField(value=inputValue, onValueChange={ inputValue = it }) | |
Button(text="Add") { | |
todoList += Item( | |
id = getId(), | |
text = inputValue, | |
isComplete = false | |
) | |
inputValue = "" | |
} | |
} | |
// utility for creating unique Id | |
var id = 0; | |
fun getId() = id++ | |
@Composable fun TodoItem(item) { | |
val index = todoList.indexOf { it == item } | |
TextField(value=item.text, onValueChange={ | |
todoList[index] = item.copy(text = it) | |
}) | |
Checkbox(value=item.isComplete, onValueChange={ | |
todoList[index] = item.copy(isComplete = !item.isComplete) | |
}) | |
Button(text="X") { | |
todoList.removeAt(index) | |
} | |
} | |
// Section 4: ToDo List Selectors | |
enum class Filter { All, Completed, Uncompleted } | |
var todoListFilter by mutableStateOf(Filter.All) | |
val filteredTodoList get() = when (todoListFilter) { | |
Filter.Completed -> todoList.filter { it.isComplete } | |
Filter.Uncompleted -> todoList.filter { !it.isComplete } | |
Filter.All -> todoList | |
} | |
@Composable fun TodoList() { | |
TodoListStats() | |
TodoListFilters() | |
TodoItemCreator() | |
filteredTodoList.forEach { | |
key(it.id) { TodoItem(item=it) } | |
} | |
} | |
@Composable fun TodoListFilters() { | |
Text("Filter:") | |
Select( | |
options=Filter.values(), | |
value=todoListFilter, | |
onChange={ todoListFilter = it } | |
) { | |
when (it) { | |
Filter.Completed -> Text("Completed") | |
Filter.Uncompleted -> Text("Uncompleted") | |
Filter.All -> Text("All") | |
} | |
} | |
} | |
@Composable fun TodoListStats() { | |
val totalNum = filteredTodoList.length | |
val totalCompletedNum = filteredTodoList.count { it.isComplete } | |
val totalUncompletedNum = totalNum - totalCompletedNum | |
val percentCompleted = if (totalNum == 0) 0 else totalCompletedNum / totalNum | |
val formattedPercentCompleted = round(percentCompleted * 100) | |
Column { | |
Text("Total items: $totalNum") | |
Text("Items completed: $totalCompletedNum") | |
Text("Items not completed: $totalUncompletedNum") | |
Text("Percent completed: $formattedPercentCompleted") | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment