ReactJS official page: https://facebook.github.io/react/
From wiki:
In this tutorial we will be building application for managing users and departments. For that we will create set of ReactJS components which will help us to create entities(Users, Departments) and show existing entities as tables. This entities (Departments and Users) are dependent, because User have to be assigned to department. So, on user creation we will have to select department from list of existing.
Output should be something like:
Creating a new React app in ......
Installing packages. This might take a couple minutes.
Installing react, react-dom, and react-scripts...
.........
Success! Created my-reactjs-test-app at ......my-reactjs-test-app
Inside that directory, you can run several commands:
npm start
Starts the development server.
npm run build
Bundles the app into static files for production.
npm test
Starts the test runner.
npm run eject
Removes this tool and copies build dependencies, configuration files
and scripts into the app directory. If you do this, you can’t go back!
We suggest that you begin by typing:
cd my-reactjs-test-app
npm start
Happy hacking!
$ cd my-reactjs-test-app
$ npm start
Output should be:
Compiled successfully!
You can now view my-reactjs-test-app in the browser.
Local: http://localhost:3000/
On Your Network: http://172.26.142.32:3000/
Note that the development build is not optimized.
To create a production build, use npm run build.
It should show something like:
Functional style is more simple and short. And it's good for "stateless"components: for simple components which are just UI elements without any logic related with component state:
or as CONST:
Class-style is more complex, but it has much more additional features related with component state:
- name for "Hello" will be taken from properties:
Now we can use use this component with different values of property "name":
Important things here:
List of columns is passed by columns parameter. We are iterating this list and creating for every element of the list corresponding pair of elements: Fileld Label -> Editor.
If we have no data for table (in objectList) we are just return null.
We have to create function which will be handlers for department and user creation. This functions will be updating state object. We will pull then "down" to low level components. For department creation, we need to update additional selector object, which will be used for user creation. Also we have not to forget to set up in constructor the initial state with empty objects.
From wiki:
React (sometimes styled React.js or ReactJS) is an open-source JavaScript library[2] for building user interfaces.
It is maintained by Facebook, Instagram and a community of individual developers and corporations.[3][4][5] According to JavaScript analytics service Libscore, React is currently being used on the websites of Netflix, Imgur, Buffer, Bleacher Report, Feedly, Airbnb, SeatGeek, HelloSign, Walmart, and others.[6]
React allows developers to create large web applications that use data which can change over time, without reloading the page. Its main goal is to be fast, simple and scalable. React processes only user interfaces in applications. This corresponds to View in the Model-View-Controller (MVC) template, and can be used in combination with other JavaScript libraries or frameworks in MVC, such as AngularJS.[7]
1. Intro
So what is ReactJS? In shorts, it's a component-based library for building rich web UI. Main concept: UI elemnts(components) has to be built from more simple elements(components).In this tutorial we will be building application for managing users and departments. For that we will create set of ReactJS components which will help us to create entities(Users, Departments) and show existing entities as tables. This entities (Departments and Users) are dependent, because User have to be assigned to department. So, on user creation we will have to select department from list of existing.
2. Create application
To start working with ReactJS we have to do just a few things:2.1. First of all we need nodeJS module create-react-app. To install it:
$ npm install -g create-react-app2.2. After it installation we can to create our ReactJS application by running:
$ create-react-app my-reactjs-test-appOutput should be something like:
Creating a new React app in ......
Installing packages. This might take a couple minutes.
Installing react, react-dom, and react-scripts...
.........
Success! Created my-reactjs-test-app at ......my-reactjs-test-app
Inside that directory, you can run several commands:
npm start
Starts the development server.
npm run build
Bundles the app into static files for production.
npm test
Starts the test runner.
npm run eject
Removes this tool and copies build dependencies, configuration files
and scripts into the app directory. If you do this, you can’t go back!
We suggest that you begin by typing:
cd my-reactjs-test-app
npm start
Happy hacking!
2.3. Now we can run NodeJS server by executing 2 last suggested steps:
$ cd my-reactjs-test-app$ npm start
Output should be:
Compiled successfully!
You can now view my-reactjs-test-app in the browser.
Local: http://localhost:3000/
On Your Network: http://172.26.142.32:3000/
Note that the development build is not optimized.
To create a production build, use npm run build.
2.4. Open browser
Now we can open suggested URL in browser: http://localhost:3000/It should show something like:
3. JSX
To make this components composition process simple, ReactJS provide new syntax: JSX - it's a combination HTML tags with Javscript:
When component was created - we can use it in another components:
class SimpleComponent extends React.Component {
render() {
return (
<div>
<h1>{10+1}</h1>
</div>
);
}
}
As you can see, HTML tags (<div> and other child tags) are located inside Javascript function. And also this HTML block has embedded Javascript block {10+1}. When component was created - we can use it in another components:
class ComplexComponent extends React.Component {
render() {
return (
<div>
<h1>Several simple components:</h1>
<SimpleComponent/>
<SimpleComponent/>
</div>
);
}
}
render() {
return (
<div>
<h1>Several simple components:</h1>
<SimpleComponent/>
<SimpleComponent/>
</div>
);
}
}
4. Components
There are 2 ways of components definition: functional-component style and class-component style.Functional style is more simple and short. And it's good for "stateless"components: for simple components which are just UI elements without any logic related with component state:
function Welcome(props) { return <h1>Hello, {props.name}</h1>; }
or as CONST:
const renderLabelForColumn = column => (<label className="formLabel">{column.label}:</label>);
Class-style is more complex, but it has much more additional features related with component state:
class ObjectList extends Component { constructor(props) { super(props); this.handleObjectCreation = this.handleObjectCreation.bind(this); } handleObjectCreation(obj) { this.props.handleObjectCreation(obj); } render() { return renderObjectListForm(this.props.objectType, this.props.title, this.props.columns, this.props.objectList, this.handleObjectCreation, this.props.selectors); } }
5. Properties and states
5.1 Properties
User components can have custom properties just like regular HTML components:function Welcome(props) { return <h1>Hello, {props.name}</h1>; }
- name for "Hello" will be taken from properties:
Now we can use use this component with different values of property "name":
function App() { return ( <div> <Welcome name="Joe" /> <Welcome name="Huan" /> <Welcome name="Sebastyan" /> </div> ); }
Important things here:
- properties are "read-only". For dynamic things like saving current state we have to use states.
- as a property we can also pass handler function from level "above":
const renderAddObjectForm = (objectType, columns, selectors, submitHandler) => ( <div className="addObject"> <form onSubmit={submitHandler}> </form> </div> )
5.2. States
In contrary to properties, states are needed for holding some data which can be changed. Main operations with states are:- we can set default value for state in constructor (in example below it's an empty array for userList property).
- we can change the state using setState method (function which is doing that can be pulled done by component hierarchy to lower level)
- we can pass state object as a property to components on lower levels of hierarchy(in example below we are passing userList from state to component ObjectList as property with name objectList)
function userCreation(user) { this.setState( prevState => { var list = prevState.userList; list.push(user); this.setState({userList: list}); } ); } class Body extends Component { constructor(props) { super(props); this.handleUserCreation = userCreation.bind(this); this.state = {userList: []}; } render() { return ( <div> <ObjectList objectList={this.state.userList} handleObjectCreation={this.handleUserCreation} /> </div> ); } }
6. Coding: components for new entity creation
Now let's start with coding. First of all let's create a directory "components" inside "src" directory. And a new JavaScript file: "AddObjectForm.js"6.1. Lets create a helper function for extracting values from form.
function getFormValue(form, valueName) { var result; for (var i = 0; i < form.elements.length; i++) { if (form.elements[i].name === valueName) result = form.elements[i].value; } return result; }
6.2. Component for rendering column labels.
const renderLabelForColumn = column => (<label className="formLabel">{column.label}:</label>);
6.3. Component for rendering editor field.
Here we are passing selectors object, which may contain array for displaying combobox (HTML SELECT component). If array with values is not present - we are displaying simple text field(HTML INPUT).const renderEditorForColumn = (column, selectors) => { if (selectors && selectors[column.name]) { var list = selectors[column.name]; let options = list.map((el) => <option key={el} value={el}>{el}</option> ); return (<select name={column.name}>{options}</select>); } else { return (<input name={column.name} type="text"/>); } };
6.4. Component for rendering list of pairs: Field Label -> Editor
List of columns is passed by columns parameter. We are iterating this list and creating for every element of the list corresponding pair of elements: Fileld Label -> Editor.const renderAddColumnsList = (objectType, columns, selectors) => { let rows = columns.map((column) => <div key={objectType + "-" + column.name}> {renderLabelForColumn(column)} {renderEditorForColumn(column, selectors)} </div> ); return (<div>{rows}</div>); };
6.5. Form component - container for field list
Here we creating the form with submit handler which was passed as propertyconst renderAddObjectForm = (objectType, columns, selectors, submitHandler) => ( <div className="addObject"> <form onSubmit={submitHandler}> <fieldset> <legend>Create new</legend> {renderAddColumnsList(objectType, columns, selectors)} <br/> <label>Press the button:</label> <input type="submit" values="Submit"/> </fieldset> </form> </div> )
6.6. Main class component for AddObjectForm
We are using class component, for being able to use constructor. In conctructor we can set binding for hanleSubmit function, to make it work properly in callbacks.class AddObjectForm extends Component { constructor(props) { super(props); this.handleSubmit = this.handleSubmit.bind(this); } handleSubmit(event) { event.preventDefault(); var form = event.target; var formObject = {}; this.props.columns.forEach((column) => formObject[column.name] = getFormValue(form, column.name)); this.props.onSubmit(formObject); } render() { return renderAddObjectForm(this.props.objectType, this.props.columns, this.props.selectors, this.handleSubmit); } }
7. Coding: components for showing the list of existing objects
Let's create another file in src/components: ObjectList.js7.1. Component for rendering one row of data in the table
const renderTableRow = (columns, data) => { let rowContent = columns.map((column) => <td key={column.name + "-" + data.id}>{data[column.name]}</td> ); return (<tr key={data.id}>{rowContent}</tr>); };
7.2. Component for rendering all rows
const renderTableRows = (columns, objectList) => ( objectList.map((data) => renderTableRow(columns, data)) );
7.3. Component for rendering table header
const renderTableHeader = (columns) => ( columns.map((column) => <th key={column.name}>{column.label}</th> ) );
7.4. Component for rendering table itself
If we have no data for table (in objectList) we are just return null.const renderTable = (columns, objectList) => { if (!objectList || objectList.length === 0) return null; return( <div> <h3>List of existing:</h3> <table className="objectListTable"> <tbody> <tr> {renderTableHeader(columns)} </tr> {renderTableRows(columns, objectList)} </tbody> </table> </div> ); };
7.5 Component for rendering AddObjectForm together with table of existing objects
AddObjectForm is rendered as ussual HTML component.const renderObjectListForm = (objectType, title, columns, objectList, creationHadler, selectors) => ( <div className="objectList"> <h2>{title}</h2> <AddObjectForm onSubmit={creationHadler} columns={columns} selectors={selectors} objectType={objectType}/> <br/> {renderTable(columns, objectList)} </div> );
7.6. Class component with proper hadlerBinding
class ObjectList extends Component { constructor(props) { super(props); this.handleObjectCreation = this.handleObjectCreation.bind(this); } handleObjectCreation(obj) { this.props.handleObjectCreation(obj); } render() { return renderObjectListForm(this.props.objectType, this.props.title, this.props.columns, this.props.objectList, this.handleObjectCreation, this.props.selectors); } }
8. Main App component
Now all our components are ready and we can combine them together.We have to create function which will be handlers for department and user creation. This functions will be updating state object. We will pull then "down" to low level components. For department creation, we need to update additional selector object, which will be used for user creation. Also we have not to forget to set up in constructor the initial state with empty objects.
function departmentCreation(department) { this.setState( prevState => { var list = prevState.depList; var depSelectors = prevState.selectors; var selector = depSelectors.department ? depSelectors.department : []; list.push(department); selector.push(department.name); depSelectors.department = selector; this.setState({depList: list}); this.setState({selectors: depSelectors}); } ); } function userCreation(user) { this.setState( prevState => { var list = prevState.userList; list.push(user); this.setState({userList: list}); } ); } class Body extends Component { constructor(props) { super(props); this.handleDepartmentCreation = departmentCreation.bind(this); this.handleUserCreation = userCreation.bind(this); this.state = {depList: [], userList: [], selectors: {}}; } render() { var depColumns = [{name: "id", label: "Id"}, {name: "name", label: "Name"}]; var userColumns = [{name: "id", label: "Id"}, {name: "name", label: "Name"}, {name: "emal", label: "Email"}, {name: "department", label: "Department"}]; return ( <div> <ObjectList objectList={this.state.depList} handleObjectCreation={this.handleDepartmentCreation} columns={depColumns} objectType="departments" title="Departments" /> <ObjectList objectList={this.state.userList} selectors={this.state.selectors} handleObjectCreation={this.handleUserCreation} columns={userColumns} objectType="users" title="Users" /> </div> ); } } class App extends Component { render() { return ( <div className="App"> <div className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h2>Welcome to React</h2> </div> <p className="App-intro"> To get started, edit <code>src/App.js</code> and save to reload. </p> <Body/> </div> ); } }