React-Fullstack - A new React framework for building React Full-Stack applications
shmuelhizmi
Posted on September 4, 2020
react-fullstck is a set of typescript packages for creating truly full-stack applications using only react, in this particular post I'm going to focus on the @react-fullstack/fullstack package which is a framework for creating react-application with their business-logic running on the server (in React).
Server-side executing and not rendering
"react-fullstack" is the exact opposite of server-side-rendering in "react-fullstack" instead of rendering your app in the server and running it in the client you actually render the app in the client ( plus manage user UI logic ) and run it ( manage the app business-logic ) in the server.
for example, user UI interactions will run on the client while logic related stuff like layout changes and data fetching will run on the server
All the benefits of React now in your server :)
using React in your server will make it much less static and much more reactive a lot like the move in the client from JQuery like libraries to web frameworks like React or Vue.
React components will make your code much more reusable plus stuff like the React context API will make your code data flow much more organized. and the most important part of using React in your server - you could use the entire collection of React libraries ( at least the non-dom related part of them :} ) in your server!!!
Speed advantages and limits over regular React apps
"react-fullstack" can even have some speed advantages compare to regular React applications because pages do not need to fetch data using HTTP every load.
the regular data flow goes something like that
user action -> layout-change -> new component data HTTP fetching -> layout-update
now with "react-fullstack" the data flow should look more like that
user client action -> server socket action request -> server tells the client via socket to update itself with new data
in cases where a new data is needed to update the view layout the "react-fullstack" way of updating the layout should be much faster but in places where a layout change occurs with no new data "react-fullstack" can actually be slower
"web-desktop-environment" was a great example of a project that really benefited a lot from using "react-fullstack" since he needs a tight connection between the server and the client and apart from that moving his entire server logic to react components made the server codebase much more user-readable and organized.
a "React-Fullstack" app is usually made up of three different packages
a server package - for the server
a client package - for the client react app
a shared package - for sharing the views component types use in both the server and client
we are going to start by creating a shared package for declaring all of our layout client components that the server is going to tell the client to render
Example:
// shared/src/index.tsimport{View}from"@react-fullstack/fullstack";exportconstViews={Home:{}asView<{username:string;logout:()=>void}>,// Home layout component and its propsLogin:{}asView<{login:(username:string,password:string)=>void}>,// Login layout component and its propsPrompt:{}asView<{message:string;onOk:()=>void}>,// Prompt layout component and its propsGif:{}asView<{url:string}>,// a Gif component and its props};
next, after we finished declaring all of our client components in our shared package we are going to move on to the server
// server/src/indeximportReactfrom"react";import{Render}from"@react-fullstack/render";import{ViewsProvider}from"@react-fullstack/fullstack";import{Views}from"shared-package";// import our shared packageimport{Server}from"@react-fullstack/fullstack-socket-server";constApp=()=>{const[location,setLocation]=useState<"home"|"error"|"login">("login");// example state for the current layoutconst[name,setName]=useState("");// exampke state for the user namereturn (<ViewsProvider<typeofViews>>{""}{/* View Provider that provide as with all of our shared views */}{({Home,Login,Prompt,Gif})=>{return (<>{location==="login"&&(// log in view<Loginlogin={(username,password)=>{if (password==="0000"){// the secret password is 0000 if the user give it to us log him insetName(username);setLocation("home");}else{setLocation("error");}}}/>)}{location==="home"&&(// home view<Homelogout={()=>setLocation("login")/* log out of the account */}username={name}><Gifurl="url_to_gif.gif"/></Home>)}{location==="error"&&(// error prompt view in case of a worong password<Promptmessage={"worng password"}onOk={()=>setLocation("login")}/>)}</>);}}</ViewsProvider>
);
};
Render(
// run the server on port 8485
<Serverport={8485}views={Views}>{()=><App/>/* on each connection to the server create an app */}</Server>
);
after we finished adding all of our business logic to the server its now time to create some views
// client/src/indeximportReactfrom"react";importReactDOMfrom"react-dom";import{Component}from"@react-fullstack/fullstack";import{Client}from"@react-fullstack/fullstack-socket-client"import{Views}from"shared-package";// home layout componentclassHomeextendsComponent<typeofViews["Home"]>{render(){return (<div><h1>Hello - {this.props.username}</h1>{this.props.children}<buttononClick={()=>this.props.logout()}>logout</button></div>);}}// prompt layout componentclassPromptextendsComponent<typeofViews["Prompt"]>{render(){return (<div><h1>{this.props.message}</h1>{this.props.children}<buttononClick={()=>this.props.onOk()}>ok</button></div>);}}// login layout componentclassLoginextendsComponent<typeofViews["Login"],{username:string;password:string}>{render(){return (<div><inputtype="text"onChange={(e)=>this.setState({username:e.target.value})}placeholder="username"/><inputtype="text"onChange={(e)=>this.setState({password:e.target.value})}placeholder="password"/><buttononClick={()=>this.props.login(this.state.username,this.state.password)}>
LogIn
</button></div>);}}// gif componentclassGifextendsComponent<typeofViews["Gif"]>{render(){return (<div><imgsrc={this.props.url}/></div>);}}ReactDOM.render(// connect to our server running on localhost:8485<Client<typeofViews>host="localhost"port={8485}views={{Home,Login,Prompt,Gif}}/>,
document.getElementById("root"));
and we are now finished you should now have a react application the can run on a server : )