How to use async/await in React props
A new way for bi-directional communication between react components.
Given two react components Parent and Child, we want to find a way to communicate from the child to the parent and vice-versa.
Child to parent
This is pretty forward, we could simply pass an event handler/function from the parent to the child and let the child trigger it when needed:
function Child({ onAlert }) { return ( <div onClick={onAlert} style={{ border: "1px dotted" }}> Click to alert the parent </div> );}function Parent() { function handleChildAlert() { alert("From child"); } return <Child onAlert={handleChildAlert} />;}
Parent to child
Unlike the previous case, this one is a little bit cumbersome, to trigger child event from parent component we can use React Refs as follow:
const Child = forwardRef((props, ref) => { useImperativeHandle(ref, () => ({ getAlert() { alert("getAlert from Child"); } })); return <h1>Child</h1>;});function Parent() { const childRef = useRef(); funciton handleClick() { childRef.current.getAlert()
} return ( <div> <Child ref={childRef} /> <button onClick={handleClick}>Click to alert child</button> </div> );}
Await Parent prop from Child
Let’s create a very simple TODO app with only two components:
- Todo: with an input, a button to fetch todo and a div to show it
- App: to fetch todo by id from jsonplaceholder rest API.
Todo is the child and App is the parent component.
Todo will have its internal state to store:
todo
: an object with API JSON response.todoId
: an integer with input value.
It also will call and await the prop onFetchTodo
from the parent, passing todoId
as an argument as follow:
function Todo({ onFetchTodo }) { const [todo, setTodo] = useState({}); const [todoId, setTodoId] = useState(1); async function handleFetchTodo() { const todo = await onFetchTodo(todoId); if (todo) { setTodo(todo); } else { console.log("Fetch error"); // handle error } } function handleInputChange(event) { setTodoId(event.target.value); } return ( <div> <input value={todoId} onChange={handleInputChange} type="number" /> <button onClick={handleFetchTodo}>Fetch todo</button> <div className="todo"> {todo.title} </div> </div> );
}
App will fetch the todo related to the passed todoId
:
function App() {
async function handleFetchTodo(todoId) {
try {
const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId}`); const todo = await response.json(); return todo; } catch (e) { console.log(e); } return null; }return (
<div className="App">
<Todo onFetchTodo={handleFetchTodo} />
</div> );}
That’s it! A workable example is provided with the following codesandbox:
By using async/await props we can not only achieve a smooth bi-directional component communication, but also we can create a generic react component/library to handle the business logic and let the final user provide her own API and data handling (mapping/reducing).