Do not mutate the state directly instead create a copy of it and work on that. React will not know the mutated state and since the object reference will keep the same value, react doesn’t create a new render when the state is updated.
Also mutating the state can cause to loss of value due to react’s ASYNC behaviour on state updates.
const [isloading, setIsLoading] = useState(false)
const handleClick = () => {
// Wrong practice
setState(true)
//Correct Way
setState((prevState) => !prevState))
}
//Anoter Example
const [posts, setPosts] = useState([{some array of object}])
const handleNewPost = () => {
setPosts((prevState) => {
const newPost = {
key: value,
key2: value
}
return [...prevState, newPost]
})
}
const handleDeletePost = (someId) => {
setPosts((prevState) => prevState.filter((post) => post.someId !== someId))
}
If there will be no manipulation on the state, if you need just a read-only value use useRef hook instead of useState and you can mutate useRef hook, it’s a mutable object and returns a single property.
useRef can be helpful to collect input value. Also it doesn’t cause a re-render.
const inputRef = useRef()
inputRef.current.value = `you can mutate`
useReducer is another react hook that we can use store and manipulate the states in React. The idea is the same with useState but if we are dealing with the multiple complex state changes in the same component, useReducer can be helpful to handle all the states from one place instead of having multiple states handling with useState.
// useReducer needs 2 arguments, reducer function and the initialState. Reducer function is executed by React when we dispatch an action. It returns an array with 2 elements which are the state and the dispatch function that allows us to trigger a state change.
const [state, dispatch] = useReducer(reducerFunction, initialState)
// We should create reducerFunction based on our requirements and create a custom state logic. It takes 2 arguments, firts one is the state, current state, and the second one is the action and based on the action type, we can manipulate the state(s).
function reducerFunction(state, action){
// Create conditional calculation, you can either use if states or switch-case
if(action.type === 'THIS_ACTION'){
return {
// Always remember, do not mutate the state
...state,
isAnythin : action.payload
}
if(action.type === 'LOADING'){
return{
...state,
isLoading: true
}
return state
}
// Then we can use our reducer to dispatch the actions.
const handleSomething = (someDataThatWeNeed) => {
dispatch({type: 'LOADING'})
// Also we can provide payload with the actions to update the state
dispatch({type: 'THIS_ACTION', payload: someDataThatWeNeed.something})
}
In react, we render our component inside the root element that’s coming with react by default, if we don’t change the default. But it’s not always good to render every element inside the same root element, the most popular examples are modals, toasts, tooltips and popups. By using createPortal , we can achieve to render the elements at any locations that we want in the DOM(Document Object Model). To achieve that;
index.html
// We should create a new entry point into our index.html file to create a new locations for our elements to render
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/logo.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ReactTSShop</title>
</head>
<body>
<div id="modal"></div> // This is our custom div element outside of the root
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
modal.jsx
// We should wrap our return object with the createPortal and it takes 2nd argument which is the location that we want to render. We can use the DOM method that comes with the vanilla JS to select the location we want in the DOM.
import { createPortal } from 'react-dom';
const Modal = () => {
return createPortal(
<>
Some JSX elements that we want to render in our modal
</>,
document.getElementById('modal') // DOM element selector
);
}