Last active
September 25, 2019 18:03
-
-
Save mattboldt/b1fbeb8d562a5edfbf2d922ac96fddc8 to your computer and use it in GitHub Desktop.
React Classes v.s. Hooks when communicating with a REST api
This file contains hidden or 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 List = () => { | |
// Items would come from an API | |
const items = [{ id: 1, active: true, complete: false }, ...] | |
return ( | |
<ul> | |
{items.map((item) => ( | |
<ListItem item={item} /> | |
))} | |
</ul> | |
) | |
} | |
// CLASS BASED VERSION | |
class ListItem extends Component { | |
state = { | |
active: props.item.active, | |
complete: props.item.complete, | |
} | |
// Request is sent as a callback after state updates | |
updateItem = () => { | |
fetch(`http://my-api/items/${this.props.item.id}`, { | |
method: 'PUT', | |
body: { item: this.state }, | |
}) | |
} | |
render() { | |
return ( | |
<li> | |
Active: | |
<input | |
type="checkbox" | |
checked={this.state.active} | |
onChange={(e) => this.setState({ active: e.target.checked }, this.updateItem)} | |
/> | |
Complete: | |
<input | |
type="checkbox" | |
checked={this.state.complete} | |
onChange={(e) => this.setState({ complete: e.target.checked }, this.updateItem)} | |
/> | |
</li> | |
) | |
} | |
} | |
// HOOKS VERSION | |
const ListItem = ({ item }) => { | |
const [active, setActive] = useState(item.active) | |
const [complete, setComplete] = useState(item.complete) | |
// useEffect fires on mount AND update, which is not desired. | |
// Can be fixed by using a custom hook, but the React docs say this is rare? Is this an anti-pattern? | |
// https://reactjs.org/docs/hooks-faq.html#can-i-run-an-effect-only-on-updates | |
// | |
// Furthermore, upon testing this with a custom useDidUpdate hook, my state was not always in sync | |
// and the PUT requests were sending stale data. Idk if this flow is appropriate for my use case? | |
useEffect(() => { | |
fetch(`http://my-api/items/${item.id}`, { | |
method: 'PUT', | |
body: { item: { active, complete } }, | |
}) | |
}, [active, complete]) | |
return ( | |
<li> | |
Active: | |
<input type="checkbox" checked={active} onChange={(e) => setActive(e.target.checked)} /> | |
Complete: | |
<input type="checkbox" checked={complete} onChange={(e) => setComplete(e.target.checked)} /> | |
</li> | |
) | |
} | |
// Potential fix: | |
const reducer = (state, action) => { | |
switch(action.type) { | |
case 'setActive': | |
return { ...state, active: action.value }; | |
case 'setComplete': | |
return { ...state, complete: action.value }; | |
default: | |
return state; | |
} | |
} | |
const ListItem = ({ item }) => { | |
const [state , dispatch] = useReducer(reducer, { active: item.active, complete: item.complete }); | |
useEffect(() => { | |
if(state.active !== item.active || state.complete !== item.complete) { | |
fetch(`http://my-api/items/${item.id}`, { | |
method: 'PUT', | |
body: { item: state }, | |
}); | |
} | |
}, [state]); | |
const updateActive = (e) => { | |
dispatch({ type: 'setActive', value: e.target.checked }); | |
} | |
const updateComplete = (e) => { | |
dispatch({ type: 'setComplete', value: e.target.checked }); | |
} | |
return ( | |
<li> | |
Active: | |
<input type="checkbox" checked={state.active} onChange={updateActive} /> | |
Complete: | |
<input type="checkbox" checked={state.complete} onChange={updateComplete} /> | |
</li> | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment