Skip to content

Instantly share code, notes, and snippets.

@mfrancois3k
Last active June 8, 2022 06:40
Show Gist options
  • Save mfrancois3k/823527cdd9a49eeb072332b1680ce39e to your computer and use it in GitHub Desktop.
Save mfrancois3k/823527cdd9a49eeb072332b1680ce39e to your computer and use it in GitHub Desktop.
React State Management
App.js
import React, { useState } from 'react';
import { usePicture } from './usePicture';
export const App = () => {
let [date, setDate] = useState('2020-05-05');
let picture = usePicture(date);
if (!picture) return <div>Loading...</div>;
return (
<div>
<input
type="date"
value={date}
onChange={(e) => setDate(e.target.value)}
/>
<h3>{picture.title}</h3>
<div>{picture.explanation}</div>
<img src={picture.url} alt={picture.title} />
</div>
);
};
usePicture.js
import { useState, useEffect } from 'react';
const fetchPicture = async (date, setPicture) => {
try {
let response = await fetch(
`https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY&date=${date}`,
);
let json = await response.json();
setPicture(json);
} catch (e) {
console.error(e);
return null;
}
};
export const usePicture = (date) => {
let [picture, setPicture] = useState();
useEffect(() => {
fetchPicture(date, setPicture);
}, [date]);
return picture;
};
// Referencce
https://github.com/manucho007/UltimateCourses/tree/master/react-state-management/course/src
import { atom, useRecoilState } from 'recoil';
// An atom is basically a piece of state
const pageState = atom({
key: 'pageState',
default: 'Home',
});
export const usePageState = () => useRecoilState(pageState);
-recoil
import React from 'react';
import { RecoilRoot } from 'recoil';
import { usePageState } from './usePageState';
const pages = ['Home', 'About'];
const Navbar = () => {
let [page, setPage] = usePageState();
return (
<ul>
{pages.map((name) => (
<li
key={name}
onClick={() => setPage(name)}
style={{ color: page === name ? 'red' : 'black' }}
>
{name}
</li>
))}
</ul>
);
};
const BlogPost = () => {
console.log('rendering blog post');
return <div>Here's a blog post</div>;
};
const Footer = () => {
let [page] = usePageState();
return <div>This is the footer for {page}</div>;
};
const App = () => {
return (
<div>
<Navbar />
<BlogPost />
<Footer />
</div>
);
};
export const AppContainer = () => {
return (
<RecoilRoot>
<App />
</RecoilRoot>
);
};
app.js
import React from 'react';
import { Provider, useDispatch, useSelector } from 'react-redux';
import store from './store';
export const App = () => {
const dispatch = useDispatch();
const todoState = useSelector((state) => state.todos);
return (
<div>
<input
type="text"
value={todoState.todo}
onChange={(e) =>
dispatch({ type: 'TODO_TYPING', todo: e.target.value })
}
onKeyUp={(e) =>
e.keyCode === 13
? dispatch({ type: 'ADD_TODO', todo: e.target.value })
: null
}
/>
<ul>
{todoState.todos.map((todo) => (
<li key={todo}>{todo}</li>
))}
</ul>
</div>
);
};
export const AppContainer = () => (
<Provider store={store}>
<App />
</Provider>
);
store.js
import { createStore, combineReducers } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
const todos = (state = { todos: [], todo: '' }, action) => {
switch (action.type) {
case 'TODO_TYPING': {
return {
...state,
todo: action.todo,
};
}
case 'ADD_TODO': {
return {
...state,
todo: '',
todos: [...state.todos, action.todo],
};
}
default:
return state;
}
};
export default createStore(combineReducers({ todos }), composeWithDevTools());
app.js
import React, { useCallback, useState } from 'react';
import { useComplete } from './useComplete';
export const App = () => {
const [clicked, setClicked] = useState();
let hello = 'hello';
let completeCallback = useCallback((data) => console.log(data + hello), [
hello,
]);
useComplete(completeCallback);
return (
<div
onClick={() => {
setClicked(!clicked);
}}>
Hello
</div>
);
};
useComplete.js
import React, { useEffect } from 'react';
export const useComplete = (completedRequest) => {
useEffect(() => {
// Network Request
completedRequest('test Data');
}, [completedRequest]);
};
Content.js
import React from 'react';
import { useAppContext } from './appContext';
export const Content = () => {
let { theme } = useAppContext();
return (
<div style={{ color: theme === 'dark' ? 'black' : 'red' }}>
Here's our main content
</div>
);
};
SideBar.js
import React from 'react';
import { ThemeControl } from './ThemeControl';
export const Sidebar = () => {
return (
<div>
<h2>Sidebar</h2>
<ThemeControl />
</div>
);
};
themeControl.js
import React from 'react';
import { useAppContext } from './appContext';
export const ThemeControl = () => {
let { theme, setTheme } = useAppContext();
return (
<div onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
Toggle the theme!
</div>
);
};
AppContext
import React, { useContext, useState } from 'react';
const AppContext = React.createContext();
export const useAppContext = () => useContext(AppContext);
export const AppProvider = ({ children }) => {
let [state, setState] = useState({});
return (
<AppContext.Provider
value={{ ...state, setTheme: (theme) => setState({ ...state, theme }) }}
>
{children}
</AppContext.Provider>
);
};
App.js
import React from 'react';
import { useDarkMode } from './useDarkMode';
export const App = () => {
let isDarkMode = useDarkMode();
return (
<div
style={{
height: 500,
width: 500,
color: isDarkMode ? 'white' : 'black',
backgroundColor: isDarkMode ? 'black' : 'white',
}}
>
Here's some content
</div>
);
};
useDarkMode.js
import { useEffect, useState } from 'react';
let initiallyDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
export const useDarkMode = () => {
let [dark, setDark] = useState(initiallyDark);
const listener = (event) => {
setDark(event.matches);
};
useEffect(() => {
window
.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', listener);
return () =>
window
.matchMedia('(prefers-color-scheme: dark)')
.removeEventListener('change', listener);
}, []);
return dark;
}
whats is matchMedia query
it alow u to target the window of the dom based on events like breakpoint
strings or event targets
https://webdevetc.com/blog/matchmedia-events-for-window-resizes/
App.js
import React, { useState } from 'react';
import { usePicture } from './usePicture';
export const App = () => {
let [date, setDate] = useState('2020-05-05');
let { picture, loading } = usePicture(date);
if (loading) return <div>Loading!</div>;
return (
<div>
<input
type="date"
value={date}
onChange={(e) => setDate(e.target.value)}
/>
<h3>{picture.title}</h3>
<div>{picture.explanation}</div>
<img src={picture.url} alt={picture.title} />
</div>
);
};
useNetwork.js
import { useState, useEffect } from 'react';
export const useNetwork = ({ url }) => {
let [state, setState] = useState({ loading: true });
useEffect(() => {
setState({ loading: true });
const makeRequest = async () => {
try {
let response = await fetch(url);
let data = await response.json();
setState({ data, loading: false });
} catch (error) {
setState({ error, loading: false });
}
};
makeRequest();
}, [url]);
return state;
};
usePictures.js
import { useNetwork } from './useNetwork';
export const usePicture = (date) => {
let { data, loading } = useNetwork({
url: `https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY&date=${date}`,
});
return { picture: data, loading };
};
App.js
import React, { useState } from 'react';
import { usePicture } from './usePicture';
export const App = () => {
let [date, setDate] = useState('2020-05-05');
let { picture, loading } = usePicture(date);
if (loading) return <div>Loading!</div>;
return (
<div>
<input
type="date"
value={date}
onChange={(e) => setDate(e.target.value)}
/>
<h3>{picture.title}</h3>
<div>{picture.explanation}</div>
<img src={picture.url} alt={picture.title} />
</div>
);
};
useNetwork.js
import { useState, useEffect } from 'react';
export const useNetwork = ({ url }) => {
let [state, setState] = useState({ loading: true });
useEffect(() => {
setState({ loading: true });
const makeRequest = async () => {
try {
let response = await fetch(url);
let data = await response.json();
setState({ data, loading: false });
} catch (error) {
setState({ error, loading: false });
}
};
makeRequest();
}, [url]);
return state;
};
UsePictures.js
import { useMemo, useState } from 'react';
import { useNetwork } from './useNetwork';
export const usePicture = (date) => {
const [test] = useState('test');
let options = useMemo(
() => ({
method: 'GET',
headers: { test },
url: `https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY&date=${date}`,
}),
[test, date]
);
let { data, loading } = useNetwork(options);
return { picture: data, loading };
};
import React, { useReducer } from 'react';
const reducer = (state, action) => {
switch (action.type) {
case 'buttonClick':
return { ...state, count: state.count + 1 };
case 'setUsername':
return { ...state, username: action.username };
default:
return state;
}
};
export const App = () => {
let [state, dispatch] = useReducer(reducer, { count: 0, username: '' });
return (
<div>
<button onClick={() => dispatch({ type: 'buttonClick' })}>
Click me
</button>
current count is {state.count}
<input
type='text'
value={state.username}
onChange={(e) =>
dispatch({ type: 'setUsername', username: e.target.value })
}
/>
Your current username is {state.username}
</div>
);
};
UseState
Status.js
import React, { useState } from 'react';
export const Status = ({ onEnter }) => {
let [message, setMessage] = useState('');
return (
<input
type="text"
value={message}
onKeyUp={(e) => {
if (e.keyCode === 13) {
onEnter(message);
setMessage('');
}
}}
onChange={(e) => setMessage(e.target.value)}
/>
);
};
App.js
import React, { useState } from 'react';
import { Status } from './Status';
export const App = () => {
let [messages, setMessages] = useState(['test', 'test2']);
return (
<div>
<Status onEnter={(value) => setMessages([value, ...messages])} />
<ul>
{messages.map((message) => (
<li key={message}>{message}</li>
))}
</ul>
</div>
);
};
app.js
import React from 'react';
import { useStorage } from './useStorage';
export const App = () => {
let [count, setCount] = useStorage('count', 0);
return <div onClick={() => setCount(count + 1)}>The count is: {count}</div>;
};
useStorage.js
import { useState, useEffect } from 'react';
export const useStorage = (key, initialState) => {
let [state, setState] = useState(initialState);
useEffect(() => {
let existingState = localStorage.getItem(key);
if (existingState) setState(JSON.parse(existingState));
}, [key]);
return [
state,
(state) => {
setState(state);
localStorage.setItem(key, JSON.stringify(state));
},
];
};
useScrollToBottom.js
import { useRef, useEffect } from 'react';
export const useScrollToBottom = (messages) => {
let scrollContainer = useRef();
useEffect(() => {
if (!scrollContainer?.current) return;
scrollContainer.current.scrollTo(0, scrollContainer.current.scrollHeight);
}, [messages]);
return scrollContainer;
};
useFakeMessage.js
import { useEffect } from 'react';
export const useFakeMessage = ({
setMessages,
message,
from = 'Test',
timeout = 5000,
}) => {
useEffect(() => {
setTimeout(() => {
setMessages((messages) => [
...messages,
{ id: messages.length + 1, content: message, from },
]);
}, timeout);
}, [setMessages, message, from, timeout]);
};
useFakeConvo.js
export const useFakeConvo = (setMessages) => {
useFakeMessage({ setMessages, message: 'That is cool!', timeout: 1000 });
useFakeMessage({
setMessages,
message: 'I know right?',
from: 'me',
timeout: 3000,
});
useFakeMessage({
setMessages,
message: 'So what should we do now....',
});
useFakeMessage({
setMessages,
message: 'So what should we do now....',
});
useFakeMessage({
setMessages,
message: 'So what should we do now....',
});
useFakeMessage({
setMessages,
message: 'So what should we do now....',
});
useFakeMessage({
setMessages,
message: 'I guess we should test scroll positioning',
from: 'me',
timeout: 9000,
});
};
Message.js
import React from 'react';
export const Message = ({ message }) => {
return (
<div style={message.from === 'me' ? styles.sent : styles.received}>
<div>{message.content}</div>
</div>
);
};
const container = {
backgroundColor: 'black',
borderRadius: 8,
padding: 12,
marginBottom: 6,
marginTop: 6,
marginRight: 12,
marginLeft: 12,
color: '#FFF',
width: '80%',
};
const styles = {
sent: {
...container,
alignSelf: 'flex-end',
},
received: {
...container,
},
};
Input.js
import React from 'react';
export const Input = ({ value, onEnter, onChange }) => {
return (
<textarea
style={{ padding: 12 }}
value={value}
onChange={(e) => onChange(e.target.value)}
onKeyUp={(e) => (e.keyCode === 13 ? onEnter(e.target.value) : null)}
/>
);
};
App.js
import React, { useState } from 'react';
import { Message } from './Message';
import { Input } from './Input';
import { useFakeConvo } from './useFakeConvo';
import { useScrollToBottom } from './useScrollToBottom';
const initialMessages = [
{ id: 1, content: 'Hello there!', from: 'me' },
{ id: 2, content: 'How are you doing?', from: 'Steven' },
{ id: 3, content: 'Pretty Good', from: 'me' },
];
export const App = () => {
let [messages, setMessages] = useState(initialMessages);
let [currentMessage, setCurrentMessage] = useState('');
useFakeConvo(setMessages);
let scrollRef = useScrollToBottom(messages);
return (
<div style={styles.wrapper}>
<div style={styles.container} ref={(ref) => (scrollRef.current = ref)}>
{messages.map((message) => (
<Message key={message.id} message={message} />
))}
</div>
<Input
value={currentMessage}
onChange={(content) => setCurrentMessage(content)}
onEnter={(content) => {
setCurrentMessage('');
setMessages([
...messages,
{ id: messages.length + 1, content, from: 'me' },
]);
}}
/>
</div>
);
};
const styles = {
wrapper: {
display: 'flex',
height: '100%',
flexDirection: 'column',
justifyContent: 'flex-end',
},
container: {
display: 'flex',
overflow: 'scroll',
height: 'max-content',
flexDirection: 'column',
},
};
useScrollToBottom.js
import { useRef, useEffect } from 'react';
export const useScrollToBottom = (messages) => {
let scrollContainer = useRef();
useEffect(() => {
if (!scrollContainer?.current) return;
scrollContainer.current.scrollTo(0, scrollContainer.current.scrollHeight);
}, [messages]);
return scrollContainer;
};
useFakeMessage.js
import { useEffect } from 'react';
export const useFakeMessage = ({
setMessages,
message,
from = 'Test',
timeout = 5000,
}) => {
useEffect(() => {
setTimeout(() => {
setMessages((messages) => [
...messages,
{ id: messages.length + 1, content: message, from },
]);
}, timeout);
}, [setMessages, message, from, timeout]);
};
useFakeConvo.js
export const useFakeConvo = (setMessages) => {
useFakeMessage({ setMessages, message: 'That is cool!', timeout: 1000 });
useFakeMessage({
setMessages,
message: 'I know right?',
from: 'me',
timeout: 3000,
});
useFakeMessage({
setMessages,
message: 'So what should we do now....',
});
useFakeMessage({
setMessages,
message: 'So what should we do now....',
});
useFakeMessage({
setMessages,
message: 'So what should we do now....',
});
useFakeMessage({
setMessages,
message: 'So what should we do now....',
});
useFakeMessage({
setMessages,
message: 'I guess we should test scroll positioning',
from: 'me',
timeout: 9000,
});
};
Message.js
import React from 'react';
export const Message = ({ message }) => {
return (
<div style={message.from === 'me' ? styles.sent : styles.received}>
<div>{message.content}</div>
</div>
);
};
const container = {
backgroundColor: 'black',
borderRadius: 8,
padding: 12,
marginBottom: 6,
marginTop: 6,
marginRight: 12,
marginLeft: 12,
color: '#FFF',
width: '80%',
};
const styles = {
sent: {
...container,
alignSelf: 'flex-end',
},
received: {
...container,
},
};
chatReducer.js
import { useReducer } from 'react';
const initialMessages = [
{ id: 1, content: 'Hello there!', from: 'me' },
{ id: 2, content: 'How are you doing?', from: 'Steven' },
{ id: 3, content: 'Pretty Good', from: 'me' },
];
const reducer = (state, action) => {
switch (action.type) {
case 'setMessages':
return { ...state, messages: action.messages };
case 'addMessage':
return {
...state,
currentMessage: '',
messages: [
...state.messages,
{
id: state.messages.length + 1,
content: action.message,
from: action.from,
},
],
};
case 'setCurrentMessage':
return { ...state, currentMessage: action.message };
default:
return state;
}
};
export const useChatReducer = () =>
useReducer(reducer, { messages: initialMessages });
useChat.js
import React, { createContext, useContext } from 'react';
import { useChatReducer } from './chatReducer';
const ChatContext = createContext();
export const ChatProvider = ({ children }) => {
let [state, dispatch] = useChatReducer();
return (
<ChatContext.Provider value={{ state, dispatch }}>
{children}
</ChatContext.Provider>
);
};
export const useChat = () => useContext(ChatContext);
Input.js
import React from 'react';
export const Input = ({ value, onEnter, onChange }) => {
return (
<textarea
style={{ padding: 12 }}
value={value}
onChange={(e) => onChange(e.target.value)}
onKeyUp={(e) => (e.keyCode === 13 ? onEnter(e.target.value) : null)}
/>
);
};
App.js
import React, { useState } from 'react';
import { Message } from './Message';
import { Input } from './Input';
import { useFakeConvo } from './useFakeConvo';
import { useScrollToBottom } from './useScrollToBottom';
const initialMessages = [
{ id: 1, content: 'Hello there!', from: 'me' },
{ id: 2, content: 'How are you doing?', from: 'Steven' },
{ id: 3, content: 'Pretty Good', from: 'me' },
];
export const App = () => {
let [messages, setMessages] = useState(initialMessages);
let [currentMessage, setCurrentMessage] = useState('');
useFakeConvo(setMessages);
let scrollRef = useScrollToBottom(messages);
return (
<div style={styles.wrapper}>
<div style={styles.container} ref={(ref) => (scrollRef.current = ref)}>
{messages.map((message) => (
<Message key={message.id} message={message} />
))}
</div>
<Input
value={currentMessage}
onChange={(content) => setCurrentMessage(content)}
onEnter={(content) => {
setCurrentMessage('');
setMessages([
...messages,
{ id: messages.length + 1, content, from: 'me' },
]);
}}
/>
</div>
);
};
const styles = {
wrapper: {
display: 'flex',
height: '100%',
flexDirection: 'column',
justifyContent: 'flex-end',
},
container: {
display: 'flex',
overflow: 'scroll',
height: 'max-content',
flexDirection: 'column',
},
};
useScrollToBottom.js
import { useRef, useEffect } from 'react';
export const useScrollToBottom = (messages) => {
let scrollContainer = useRef();
useEffect(() => {
if (!scrollContainer?.current) return;
scrollContainer.current.scrollTo(0, scrollContainer.current.scrollHeight);
}, [messages]);
return scrollContainer;
};
useFakeMessage.js
import { useEffect } from 'react';
export const useFakeMessage = ({
setMessages,
message,
from = 'Test',
timeout = 5000,
}) => {
useEffect(() => {
setTimeout(() => {
setMessages((messages) => [
...messages,
{ id: messages.length + 1, content: message, from },
]);
}, timeout);
}, [setMessages, message, from, timeout]);
};
useFakeConvo.js
export const useFakeConvo = (setMessages) => {
useFakeMessage({ setMessages, message: 'That is cool!', timeout: 1000 });
useFakeMessage({
setMessages,
message: 'I know right?',
from: 'me',
timeout: 3000,
});
useFakeMessage({
setMessages,
message: 'So what should we do now....',
});
useFakeMessage({
setMessages,
message: 'So what should we do now....',
});
useFakeMessage({
setMessages,
message: 'So what should we do now....',
});
useFakeMessage({
setMessages,
message: 'So what should we do now....',
});
useFakeMessage({
setMessages,
message: 'I guess we should test scroll positioning',
from: 'me',
timeout: 9000,
});
};
Message.js
import React from 'react';
export const Message = ({ message }) => {
return (
<div style={message.from === 'me' ? styles.sent : styles.received}>
<div>{message.content}</div>
</div>
);
};
const container = {
backgroundColor: 'black',
borderRadius: 8,
padding: 12,
marginBottom: 6,
marginTop: 6,
marginRight: 12,
marginLeft: 12,
color: '#FFF',
width: '80%',
};
const styles = {
sent: {
...container,
alignSelf: 'flex-end',
},
received: {
...container,
},
};
chatReducer.js
import { useReducer } from 'react';
const initialMessages = [
{ id: 1, content: 'Hello there!', from: 'me' },
{ id: 2, content: 'How are you doing?', from: 'Steven' },
{ id: 3, content: 'Pretty Good', from: 'me' },
];
const reducer = (state, action) => {
switch (action.type) {
case 'setMessages':
return { ...state, messages: action.messages };
case 'addMessage':
return {
...state,
currentMessage: '',
messages: [
...state.messages,
{
id: state.messages.length + 1,
content: action.message,
from: action.from,
},
],
};
case 'setCurrentMessage':
return { ...state, currentMessage: action.message };
default:
return state;
}
};
export const useChatReducer = () =>
useReducer(reducer, { messages: initialMessages });
Input.js
import React from 'react';
export const Input = ({ value, onEnter, onChange }) => {
return (
<textarea
style={{ padding: 12 }}
value={value}
onChange={(e) => onChange(e.target.value)}
onKeyUp={(e) => (e.keyCode === 13 ? onEnter(e.target.value) : null)}
/>
);
};
App.js
import React, { useState } from 'react';
import { Message } from './Message';
import { Input } from './Input';
import { useFakeConvo } from './useFakeConvo';
import { useScrollToBottom } from './useScrollToBottom';
const initialMessages = [
{ id: 1, content: 'Hello there!', from: 'me' },
{ id: 2, content: 'How are you doing?', from: 'Steven' },
{ id: 3, content: 'Pretty Good', from: 'me' },
];
export const App = () => {
let [messages, setMessages] = useState(initialMessages);
let [currentMessage, setCurrentMessage] = useState('');
useFakeConvo(setMessages);
let scrollRef = useScrollToBottom(messages);
return (
<div style={styles.wrapper}>
<div style={styles.container} ref={(ref) => (scrollRef.current = ref)}>
{messages.map((message) => (
<Message key={message.id} message={message} />
))}
</div>
<Input
value={currentMessage}
onChange={(content) => setCurrentMessage(content)}
onEnter={(content) => {
setCurrentMessage('');
setMessages([
...messages,
{ id: messages.length + 1, content, from: 'me' },
]);
}}
/>
</div>
);
};
const styles = {
wrapper: {
display: 'flex',
height: '100%',
flexDirection: 'column',
justifyContent: 'flex-end',
},
container: {
display: 'flex',
overflow: 'scroll',
height: 'max-content',
flexDirection: 'column',
},
};
to visualize ur state debugging tool
App.js
import React from 'react';
import { useTodos } from './useTodos';
export const App = () => {
let [state, send] = useTodos();
return (
<div>
<input
type="text"
value={state.context.todo}
onChange={(e) => send('TODO.TYPING', e.target)}
onKeyUp={(e) => (e.keyCode === 13 ? send('TODOS.ADD', e.target) : null)}
/>
<ul>
{state.context.todos.map((todo) => (
<li key={todo}>{todo}</li>
))}
</ul>
</div>
);
};
useTodos.js
import { useMachine } from '@xstate/react';
import { Machine, assign } from 'xstate';
const todosMachine = Machine({
id: 'todos',
context: {
todo: '',
todos: ['first item'],
},
initial: 'initializing',
states: {
initializing: {
actions: {
todos: (ctx) => ctx.todos,
},
},
},
on: {
'TODO.TYPING': {
actions: assign({ todo: (ctx, target) => target.value }),
},
'TODOS.ADD': {
actions: assign({
todo: '',
todos: (ctx, target) => [...ctx.todos, target.value],
}),
cond: (ctx, target) => target.value.trim().length,
},
},
});
export const useTodos = () => useMachine(todosMachine);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment