Skip to content

Instantly share code, notes, and snippets.

@lelandrichardson
Created August 28, 2020 14:52
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lelandrichardson/4cfbb7aab96e01803cc7b289c2303e45 to your computer and use it in GitHub Desktop.
Save lelandrichardson/4cfbb7aab96e01803cc7b289c2303e45 to your computer and use it in GitHub Desktop.
Recoil vs Compose
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>
);
}
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