In this article, we will learn about handling state without Redux. There are multiple ways to manage the state without redux in any web app. In this article, we will learn state management by useState() hook. It is a react hook that returns two values the state and a function by which we can mutate the state and we can initial state as arguments, in the code below it is an empty array. The component re-renders whenever the state changes. Let’s understand this with an example. In this web-app we will maintain the state ( which is the tasks array ) in the App component, pass down the tasks array to the Task component to display in a proper format and do the state mutation in the Tasks component with the help of function provided by useState() hook.
Syntax:
const [state,setState] = useState([]);
Creating React Application And Installing Module:
Step 1: Create a React application using the following command.
npx create-react-app foldername
Step 2: After creating your project folder i.e. foldername, move to it using the following command:
cd foldername
Step 3: Run the development server by using the following command:
npm start
Project Structure: It will look like the following.
Implementation: Add the following code in respective files.
App.js : In this component we have created a state with the help of useState() and set its initial value to an array of only one element. Also we have passed down props to the Tasks component with their respective names.
Javascript
import { useState } from "react" ; import "./App.css" ; import Tasks from "./Component/Tasks" ; function App() { const [taskList, setTaskList] = useState([ { title: "Submit DS assignment" , deadline: "1pm 10 March" , }, ]); return ( <div className= "App" > <Tasks taskList={taskList} setTaskList={setTaskList} /> </div> ); } export default App; |
Tasks.js : In this component, we have added some basic styles and hooked up handleClick() and handleChange() to the button and input fields. We have used the method to dynamically update the object property via the syntax given below. This allows us to reduce the redundancy of creating different handleChange functions for different input fields. Also, note to use this method the input field has to have a name property on it. The handleClick() function is responsible for mutating the state and adding the current task to the App’s state. We have maintained a component state which is CurrentTask it will help us keep a track of the task that the user wants to add to the list.
Javascript
[e.target.name] : e.target.value |
Javascript
import React, { useState } from "react" ; import Task from "./Task" ; const Tasks = ({ setTaskList, taskList }) => { const inputStyles = { border: "none" , borderBottom: "2px solid #9FE6A0" , outline: "none" , margin: "10px" , }; const [CurrentTask, setCurrentTask] = useState({}); let taskListDisplay = taskList.map((task, index) => { return ( <Task key={index} index={index} deadline={task.deadline} title={task.title} /> ); }); const handleClick = (e) => { setTaskList([...taskList, CurrentTask]); }; const handleChange = (e) => { e.preventDefault(); setCurrentTask({ ...CurrentTask, [e.target.name]: e.target.value, }); }; return ( <div> <h4>Task-Web-App :</h4> <div> <input style={inputStyles} placeholder= "Title" type= "text" value={CurrentTask.title !== undefined ? CurrentTask.title : "" } name= "title" onChange={handleChange} /> <input style={inputStyles} placeholder= "Deadline" type= "text" value={CurrentTask.deadline !== undefined ? CurrentTask.deadline : "" } name= "deadline" onChange={handleChange} /> <button style={{ outline: "none" , border: "none" , backgroundColor: "#9FE6A0" , borderRadius: "8px" , padding: "7px" , width: "90px" , }} onClick={handleClick} > Add Task </button> </div> <div style={{ display: "flex" , alignItems: "center" , flexDirection: "column" , }} > {taskListDisplay} </div> </div> ); }; export default Tasks; |
Task.js: This is just a presentational component that displays the task details by destructuring the values/properties.
Javascript
import React from "react" ; const Task = ({ deadline, title, index }) => { return ( <div style={{ border: "2px solid #F55C47" , width: "30vw" , margin: "10px" , borderRadius: "10px" , padding: "8px" , }} > <h2 style={{ margin: "2px" , color: "#564A4A" }}> Task {index + 1} </h2> <p> { " " } <strong style={{ color: "#564A4A" }}> Title :</strong> <span style={{ color: "#707070" }}> {title}</span> </p> <p> <strong style={{ color: "#564A4A" }}>Deadline : </strong> <span style={{ color: "#707070" }}>{deadline}</span> </p> </div> ); }; export default Task; |
Working demo of the web-app:
Example 2: In this example, we will not maintain state in the App component but rather in some other child component, it will be Songs component. We will create two states one to store the singer’s name and the second will be the array of top 5 hit songs of the singer whose name the user has entered. And we’ll be fetching data from Rapid API’s open API this will be done with the help of axios. Implementation is discussed below.
Install the axios as a dependency:
npm i axios
Filename – App.js : Since we are not maintaining app state in this component we don’t need any state in this file.
Javascript
import { useState } from "react" ; import "./App.css" ; import Songs from "./Component/Songs" ; import Tasks from "./Component/Tasks" ; function App() { return ( <div className= "App" > <Songs /> </div> ); } export default App; |
Filename – Songs.js: In this Component, we have initialized the Singer’s name to be Justin by default which means it will fetch data for Justin on the initial render. We have made a fetchData function which is responsible for fetching data and updating the Songs component HitsSongs state, this function is initially called when the app loads to fetch the hit songs of Justin with the help of useEffect hook. Note the params object is what will hold the singer’s name while fetching the hit songs for the singer. We can reuse this fetchData function when the user clicks on the Find button because it is a stand-alone function and does make use of any other data except for updating the HitSongs state. And finally, we have hooked-up the onClick and onChange handlers to the button and input.
Javascript
import React, { useState,useEffect } from "react" ; import axios from "axios" ; const Songs = () => { const [HitSongs, setHitSongs] = useState([]); const [SingerName, setSingerName] = useState( "Justin" ); const inputStyles = { border: "none" , borderBottom: "2px solid #9FE6A0" , outline: "none" , margin: "10px" , }; const fetchData = async () => { var options = { method: "GET" , params: { q: `${SingerName}` }, headers: { "x-rapidapi-host" : "genius.p.rapidapi.com" , "x-rapidapi-key" : "ffc2438edbmsh0a88b634e6e77a7p123125jsnfb163d4d72f7" , }, }; let data = await axios.request(options); data = data.data.response.hits.slice(0, 5); setHitSongs(data); }; const handleClick = (e) => { fetchData(); }; const handleChange = (e) => { e.preventDefault(); setSingerName(e.target.value); }; useEffect(() => { fetchData(); }, []); let displaySongs = HitSongs.map((song) => { return ( <div key={song.result.id}> <div style={{ border: "2px solid #9FE6A0" , width: "30vw" , padding: "5px" , margin: "8px" , }} > {song.result.full_title} </div> </div> ); }); return ( <> <div> <h4>Top 5 Hit songs :</h4> <div> <input style={inputStyles} placeholder= "Singer Name" type= "text" value={SingerName !== "" ? SingerName : "" } onChange={handleChange} /> <button style={{ outline: "none" , border: "none" , backgroundColor: "#9FE6A0" , borderRadius: "8px" , padding: "7px" , width: "90px" , }} onClick={handleClick} > Search </button> </div> <div style={{ display: "flex" , alignItems: "center" , flexDirection: "column" , }} ></div> </div> <div> {displaySongs.length === 0 ? ( <> <div> Fetching data ... Please wait 🙂 </div> </> ) : ( <div style={{ display: "flex" , flexDirection: "column" , alignItems: "center" , }} > {displaySongs} </div> )} </div> </> ); }; export default Songs; |
Working demo of the web-app: