To run this project locally:
npm i
npm start
Redux Store to manage loggedin user info
Redux store setted up with redux-toolkit and API middleware
Here is the implementation of basic API call. We use redux ducks apporach which suggests to keep all related implemantation in one file instead of seperating them as actions,reducers etc.
export const login = (data) =>
apiCall({
url: `${process.env.REACT_APP_BE_URL}/auth/login`,
method: "post",
data,
onStart: requested.type,
onSuccess: loginSuccess.type,
onError: failed.type,
});
If want to learn more about middleware implementation and using redux ducks approach you check my starter template here
Protected Route for rendering routes conditionally
We're protecting all the routes from not loggedin users. User should login or signup to see homepage and the other pages
Here is the implementation of super powerful Protected Route
const ProtectedRoute = ({ component: Component, ...rest }) => {
const dispatch = useDispatch();
const currentUser = useSelector((state) => state.user.data);
const history = useHistory();
useEffect(() => {
if (isAuthUser()) {
console.log(rest.path);
if (rest.path === "/") {
dispatch(getUserProfile());
}
}
}, []);
return (
<Route
{...rest}
render={(props) =>
isAuthUser() ? (
<Component {...props} />
) : (
<Redirect to='/login' />
)
}
/>
);
};
Woww that's really cool right. But what the hack is that. Let's see what's going there:
We're keeping a not httpOnly boolean cookie to manage user status. It doesn't contain any sensitive information about user. It just contain a boolean value to check if the user is loggedin. Evertime when we send a request to our backend and it returns a 200 or 201 response we're setting some cookies on browser. These cookies are token, refreshToken and isAuthUser. token and refreshToken cookies are httpOnly to prevent attackers to get user precious token. isAuthUser is not a httpOnly cookie, so we can manage user loggedin status using this cookie.
Here's a little helper function to check this cookie:
import { getCookie } from "./cookies";
export const isAuthUser = () => {
return getCookie("isAuthUser") === "true";
};
In our protected route we can create a condition using this cookie.
Yeapp. You now the biggest secret now. Please don't hack my websiteWhat about useEffect and dispatch? We're saying our protected route to fetch user info everytime when I call you and set my redux user state with user information. So I can get user information whatever component I want. Also we have a condition there.
if (rest.path === "/") {
dispatch(getUserProfile());
}
We have this condition to make just one API call. Otherwise this will make API call for each protected route. We want to keep our application performence well.
And the funniest part is using this ProtectedRouter. What we need to do is just using ProtectedRouter instead of normal react Route.
<Route path='/login' exact component={Login} />
<Route path='/signup' exact component={Signup} />
<ProtectedRoute path='/' exact component={Home} />
<ProtectedRoute path='/dm/message' exact component={Message} />
<ProtectedRoute path='/:user' exact component={Profile} />
.
Following-Notification-Posts-Likes
All these features respond immediately to changes. If you follow user2, you'll see that your following size will be +1 immediately. Same for likes,notification and posts. You don't need to refresh page to see changes. That's why we are using React.
DM messaging
This is a test feature which is implemented using socket.io-client. You need to talk to me if you want to make a conversation with your follers or following. It just requires adding starting chat feature to FE. We tested it our test account by creating conversation models in database it works well. This will be the next feature. Stay tuned.