π₯ Most of us Start our project without thinking about its structure and design. Later on we find difficulties as code gets longer.
π In our recent blogs we went through the Basics of Scalability, Deployment and Cloud. Don't worry we'll go in depth with π Youtube tutorials too in future, after covering the basics.
We talked about Clean coding practices, architectures etc. Today we'll talk about most wanted topic:-
π€© How to Architect our Frontend Project before even starting
π Architect/Design the Project well to avoid cluttering afterwards.
π Note:- Follow on Twitter to get daily value on Open Source, #100daysofCode for building a product end to end and much more...
π Define a Good Project directory structure
π§βπ» Let's see a good practice to define a Project directory:-
- features dir
This is similar to the pages dir but it is different in a way that pages includes different pages like HomePage etc but feature dir contains the specific feature with its components, custom hooks, services and index.js file.
index.js file is used as an API to expose limited functionality(simple export of JS exposes all the code) to the outer code of features.
- pages dir
It contains only the .js or .tsx files(single files) because logic for a page will go to the features dir and files in pages dir will just glue the different features and some general purpose components(Buttons etc)
- layouts dir
It is a dir having the layout components like navbar, sidebar etc.
- lib dir
This dir(as the name states) contains all the wrappers(our code on top of library) inside. For ex: To use Axios you can define axios.js and from there we can use axios library.
- services dir
This folder contains the code that interacts with other APIs. Having multiple API calls for a feature can be integrated inside the service dir
π₯³ Now, we know how our Project Directory should look like. Let's switch over to the Low level design or Laying out our Application workflow.
π React FLUX
As a beginner most of us don't follow a preferred flow of data or actions in the app. Means if someone click the button we directly call a function(Buy Smartphone, for say) instead of publishing an action "PRODUCT_BOUGHT". This action could be picked by some service to perform an action, Right?
π Exactly, this will make our code very fluent and clean.
THIS IS WHERE FLUX COMES INTO THE PICTURE
π€© FLUX, FLUX, FLUX. In frontend most people do not use recommended design patterns or flows and end up in a code mess.
Let's see how to design a great code flow.
π What is FLUX design and How is its flow work
React says it is just a view layer not a framework. To make it behave like a framework we use a specific kind of patterns like FLUX and REDUX. Facebook laid out this architecture.
π It looks like this:-
π§βπ» Let suppose someone clicks on the button Add Post to add his/her post in the social media. So, what can happen is we can design an action and dispatcher like:-
import dispatcher from "../appDispatcher";
import actionTypes from "./actionTypes";
import data from "../db.json";
var posts_list = [
{
"id": 1,
"title": "Hello World",
"author": "Lovepreet",
"body": "Example of blog application"
},
{
"id": 2,
"title": "Hello Again",
"author": "Singh",
"body": "Testing another component"
}
]
export function getPosts() {
dispatcher.dispatch({
actionTypes: actionTypes.GET_POSTS,
posts: posts_list,
});
}
- Now, Let's say our basic Store is:-
dispatcher.register((action) => {
switch (action.actionTypes) {
case actionTypes.GET_POSTS:
_posts = action.posts;
store.emitChange();
break;
default:
}
});
We'll register to the dispatcher and will emit/broadcast the event which someone can listen to. Here store.emitChange() is:-
class PostStore extends EventEmitter {
addChangeListener(callback) {
this.on(CHANGE_EVENT, callback);
}
removeChangeListener(callback) {
this.removeListener(CHANGE_EVENT, callback);
}
emitChange() {
this.emit(CHANGE_EVENT);
}
getPosts() {
return _posts;
}
}
and store can be defined from this as:-
const store = new PostStore();
π Now, we can see that we have store which is listening to the events/actions and getting some data/payload. It is then changing or not changing the values/state of some things and then emitting an event through Emitters.
And Now in view we can listen for the changes and can change accordingly:-
useEffect(() => {
setposts(data["posts"]);
}, []);
π To sum it up:-
[Credits:- freecodecamp]
π Now, React REDUX π€©
As, we saw React is only capable of showing something, monitoring the dom for state changes and passing props. React FLUX makes it a full framework capable of doing more than that.
π₯ Now, React REDUX is the implementation on top of FLUX and most widely used. It looks like:-
π In REDUX, we have Actions, Dispatcher and Reducers inside the store. Reducers are nothing but the functions that can be triggered on arrival of some action through dispatcher and then can change the state of the app.
π In Redux only different thing is we have reducers. A reducer can look like:-
function nextTodoId(todos) {
const maxId = todos.reduce((maxId, todo) => Math.max(todo.id, maxId), -1)
return maxId + 1
}
// Use the initialState as a default value
export default function appReducer(state = initialState, action) {
// The reducer normally looks at the action type field to decide what happens
switch (action.type) {
// Do something here based on the different types of actions
case 'todos/todoAdded': {
// We need to return a new state object
return {
// that has all the existing state data
...state,
// but has a new array for the `todos` field
todos: [
// with all of the old todos
...state.todos,
// and the new todo object
{
// Use an auto-incrementing numeric ID for this example
id: nextTodoId(state.todos),
text: action.payload,
completed: false
}
]
}
}
default:
// If this reducer doesn't recognize the action type, or doesn't
// care about this specific action, return the existing state unchanged
return state
}
}
π Depending on the functionality, an app can have multiple reducers like:
header: headerReducer
payment: paymentReducer
home: homeReducer
π₯ Our app at the end can have one root reducer, so we have to combine the different reducers:-
import { combineReducers } from 'redux'
import todosReducer from './features/todos/todosSlice'
import filtersReducer from './features/filters/filtersSlice'
const rootReducer = combineReducers({
// Define a top-level state field named `todos`, handled by `todosReducer`
todos: todosReducer,
filters: filtersReducer
})
export default rootReducer
Now, to create Store we have a basic function from redux
import { createStore } from "redux";
import { currencyReducer } from "./reducers/rootReducer";
const appStore = createStore(rootReducer);
export default appStore;
π Now, Suggest me the choice on our next Blog on:-
React project using
- FLUX
- REDUX
Choose and Comment.....
π That was it for today. Kindly Share with your friends and on your social media π₯
Top comments (5)
aaaand winner is zustand! β€π
Yeah. Best way to avoid boilerplate.
Thanks brother π
Nice
π