- Don't need Classes to use all React features.
- Provide a more direct API to the common concepts (props, state, context, refs and lifecycle).
- Reuse stateful logic to avoid Huge components.
- Don't rewrite your components (Use them from new components by using smaller functions instead).
- No breaking changes (100% backwards compatible), more details here.
A Hook is a special function that lets you “hook into” React features.
useState: is a Hook that lets you add React state to function components
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
Before
import React, { useState } from 'react';
function Example() {
// Declare a new state variable
const [count, setCount] = useState(0);
After
Returns a stateful value and a function to update it.
const App = class App extends Component {
state = {
appState: AppState.currentState
}
async componentDidMount () {
AppState.addEventListener('change', this._handleAppStateChange.bind(this))
}
componentWillUnmount () {
AppState.removeEventListener('change', this._handleAppStateChange.bind(this))
}
function useAppState() {
const [appState, setAppState] = useState(AppState.currentState)
const onChange = (newState) => setAppState(newState)
useEffect(() => {
AppState.addEventListener('change', onChange)
return () => {
AppState.removeEventListener('change', onChange)
}
}, [])
return appState
}
Or using a smaller function that can be reused from any component
// Without the second parameter
useEffect(() => {
console.log('I will run after every render');
});
// With the second parameter
useEffect(() => {
console.log('I will run only when valueA changes');
}, [valueA]);
// With zero dependencies
useEffect(() => {
console.log('I will run only once');
}, []);
Perform side effects from a function component.
// Ignore the first render
const didMountRef = useRef(false);
useEffect(() => {
if (didMountRef.current) {
console.log('I will run when an update occurs!');
} else didMountRef.current = true;
});
const MyFunComponent = (props) => {
useEffect(() => {
console.log('I will run when a prop changes!');
}, Object.keys(props).sort().map(k => props[k]));
}
The idea with this is collocate the side effects, the changes of the state in a reusable component.
// State variables can hold objects and arrays
const [todos, setTodos] = useState([{ id: 123, text: 'Learn Hooks' }]);
But unlike this.setState in a class, updating a state variable always replaces it instead of merging it...
// Remove an item by id
const deletedId = 123;
setTodos(tasks => tasks.filter(task => task.id !== deletedId))
An option to fix that would be:
The correct place to update the state and ¿notify changes?
const initialState = {
data: [],
error: null,
loading: true,
...
}
const [state, dispatch] = useReducer((state, action) => {
switch (action.type) {
case 'publish':
// This is a perfect place to save the state or call external APIs
applyChanges(state)
break;
case 'newMessage':
const newData = unionBy([action.value], [...state.data], 'id')
return { ...state, data: newData }
default:
return { ...state, ...action }
}
}, initialState)