Social Media Basics
Enable a list of users to follow and unfollow each other.
Environment
Use the environment you are most comfortable with. I recommend using create-react-app to create a local version of the project so that you can inspect the DOM easily from your browser when debugging. Alternatively, you can work from a blank React template in CodeSandbox. Starter code is provided after the problem specification.
Specification
Write a functional component to display an input field allowing the user to enter a name. When the user submits, the name should be added to a list of names displayed below the input field. Below the list, render two input fields to allow the user to enter two names, and when the user submits, the first name should follow the name. In other words, the second name should be added to the first name’s following and the first name should be added to the second name’s followers. The finished component is shown below:
When the user clicks on a name in the list, issue a browser alert that displays their following and followers counts:
Be sure to consider edge cases, e.g. if a person tries to follow themselves or someone that they already follow.
Starter Code
Here is some code to get you started:
import React, { useState } from "react";
function App() {
return <Followers />;
}
const Followers = () => {
// YOUR CODE HERE
};
export default App;
Feel free to divide the challenge into multiple subcomponents.
Solution
import React, { useState } from "react";
function App() {
return <Followers />;
}
const Followers = () => {
const [userList, setUserList] = useState([]);
const [newUser, setNewUser] = useState("");
const [user1, setUser1] = useState("");
const [user2, setUser2] = useState("");
const createUser = (e) => {
e.preventDefault();
if (!userList.some((user) => user.name == newUser)) {
setUserList([
...userList,
{ name: newUser, followers: [], following: [] },
]);
setNewUser("");
} else alert("This user already exists");
};
const submitNewFollow = (e) => {
e.preventDefault();
const user1Index = userList.findIndex((user) => user.name == user1);
const user2Index = userList.findIndex((user) => user.name == user2);
if (user1 == user2) alert(user1 + "cannot follow themselves.");
else if (user1Index == -1) alert(user1 + " is not yet a user");
else if (user2Index == -1) alert(user2 + " is not yet a user");
else if (userList[user1Index].following.includes(user2))
alert(user1 + " already follows " + user2);
else {
// create new objects to modify and insert into array copy
const newUser1Obj = { ...userList[user1Index] };
const newUser2Obj = { ...userList[user2Index] };
newUser1Obj.following = [...newUser1Obj.following, user2];
newUser2Obj.followers = [...newUser2Obj.followers, user1];
const shallowListCopy = [...userList];
shallowListCopy[user1Index] = newUser1Obj;
shallowListCopy[user2Index] = newUser2Obj;
setUserList(shallowListCopy);
setUser1("");
setUser2("");
}
};
return (
<div
style={{
display: "flex",
flexDirection: "column",
textAlign: "center",
}}
>
<form onSubmit={createUser}>
<input
style={{ width: 200, margin: 30 }}
value={newUser}
required
placeholder={"Enter new user"}
onChange={(e) => setNewUser(e.target.value)}
/>
</form>
<h4>User List</h4>
{userList.map((user) => (
<div
onClick={() => {
alert(
user.name +
" has " +
user.followers.length +
" followers and is following " +
user.following.length +
" people."
);
}}
style={{ cursor: "pointer" }}
>
{user.name}
</div>
))}
<form
style={{ display: "flex", margin: 50, alignSelf: "center" }}
onSubmit={submitNewFollow}
>
<input
style={{ width: 100 }}
value={user1}
required
onChange={(e) => setUser1(e.target.value)}
/>
<div style={{ margin: "0px 10px 0px 10px" }}>will now follow</div>
<input
style={{ width: 100 }}
value={user2}
required
onChange={(e) => setUser2(e.target.value)}
/>
<input type={"submit"} />
</form>
</div>
);
};
export default App;
The solution above first sets up four state variables — one to store an array of user objects and three to store the values of the three input fields. In the return statement, we use the map function to produce a clickable name element for each user in the userList
array. On click, an alert is issued containing the user’s followers and following counts. The bulk of the component’s logic is contained in the submit handlers for the forms. When a site visitor submits a new name, the component creates a new user and adds the user to the userList
array as long as there is no other user with the same name.
The submit handler for the second form is slightly longer. We need to ensure the user is not attempting to make a person follow themselves, that both people entered are present in the list, and that the first person entered does not already follow the second person. If these conditions are met, the appropriate users in the userList
array can be carefully updated to ensure the array is not being directly modified in the process. This requires making a shallow copy of the userList
array and copying the objects that need to be modified before finally modifying them and inserting them into the array copy.
For an additional challenge, allow the site visitor to make users unfollow each other.