Translator: the Legendary
The original link
Photo by Franco Folini
I’ve been using React for over a year now. I’m also doing training to help people learn Redux from the ground up. I noticed that I explained some of the same concepts over and over again in every practice. I think these concepts are essential if you want to “talk about React.” If you are in the middle of learning, you may be interested in reading this article.
1) It is not a framework
Angular or Ember are frameworks that have helped you make some decisions. React is just a library, and you need to make all the decisions yourself. It focuses on helping you build user interfaces using components.
It doesn’t help you communicate with the server, translate, route, etc. Some people think this is a weakness. A wise man once said:
“Frameworks solve the problem of their creators” — one of my mentors
React is thin, and it’s easy to mix with other third-party libraries. The rich JS ecosystem has a library for everything. You can choose the one you like best and plug it in without being constrained by the design of the frame.
There is, of course, famous JS Fatigue. If you think this is the case and don’t want to make every decision yourself, do a 30-minute study, choose an Opinionated starter Kits/Boilerplates, change whatever you want and just use it.
2) JSX
When you look at the React example, you’ve probably already seen JSX. But React code can also be written in pure JS code:
const rootElement =
React.createElement('div', {},
React.createElement('h1', {style: {color: 'red'}}, 'The world is yours'),
React.createElement('p', {}, 'Say hello to my little friend')
)
ReactDOM.render(rootElement, document.getElementById('app'))
Copy the code
Some people don’t like to write the entire markup code as a function call. That’s probably why people on Facebook came up with JSX – a “React. CreateElement (Component, props,… Children’s grammatical sugar method “. That’s why we can refactor the above example:
const RootElement = (
<div>
<h1 style=>The world is yours</h1>
<p>Say hello to my little friend</p>
</div>
)
ReactDOM.render(RootElement, document.getElementById('app'))
Copy the code
Babel converts markup to pure JS code during the build process.
3) this is JavaScript
To generate tokens dynamically in React, you can also use JS:
<select value={this.state.value} onChange={this.handleChange}>
{somearray.map(element => <option value={element.value}>{element.text}</option>)}
</select>
Copy the code
In the example above, the SomeArray array is mapped to the list of
Although there is no need to use it:
<select onChange={this.handleChange}>
{somearray.map(element => (
<option
value={element.value}
selected={this.state.value === element.value}
>
{element.text}
</option>
))}
</select>
Copy the code
Many popular frameworks use template languages to do this. Each framework has its own template language. So every time you want to use a framework, you need to learn a new template language and its quirks and shortcomings.
The above code comes with a warning about the missing key attribute. Want to know why and how to solve the access [here] (facebook. Making. IO/react/docs /…
I remember the first time I used Control in Angular 1. There is a special ngOptions directive that will generate all possible options for you.
<select
ng-model=""selectedItem""
ng-options=""item as item.name for item in items"">
</select>
Copy the code
So far I don’t know what item as item.name for item means.
There are people smarter than me who have memorized how to use various template languages. I often switch from the back end to the front end. For a long time, I didn’t do the front end at all. If you don’t use it, knowledge disappears. That’s why memory doesn’t work for me. But I’m familiar with the map () function and HTML, so the React approach appeals to me.
Another benefit is that static code analysis is free. It is easier to detect JS than custom template tags.
For me, templates are developed based on strings. There are no strict hints. Just some in HTML, not checked in any way. JSX adds more things to JS and makes it more powerful. This is why I disagree when people call JSX a template language.
4) It is declarative
In React, you can write components declaratively. Let’s use
<select value={this.state.value} onChange={this.handleChange}>
{somearray.map(element => <option value={element.value}>{element.text}</option>)}
</select>
Copy the code
In this
example, you don’t use a for loop to manually create a collection of maps. You’re not saying what it should do, but what it should look like.
5) Separate concerns
In React, you usually keep HTML, JS, and CSS as a component. If you want to display a Row on the grid, you create a Row component and put the HTML, logic, and behavior in a file.
Over the years, we’ve split JS, HTML and CSS into separate files. Pete Hunt calls separation technology. It should not be confused with the concept of separation of concerns.
I often get questions about whether I think this “lack of separation” is strange. But it seems strange to me that people often give examples to defend the strategy of keeping HTML and JS separate:
“If you keep the HTML and JS in separate files, you can easily replace the HTML and keep the JS intact.
If you think about it, it doesn’t work. Most changes to the HTML structure require refactoring of THE JS logic.
“Display logic and markup are unavoidably tightly coupled” — Pete Hunt
If you change the text input to a check box, you need to rewrite the logic.
6) Data transfer
In React, data is passed down the tree of components. To pass data from a parent component to a child component, you need to use props. From a JSX perspective, props is an HTML attribute.
The parent component:
<div>
<Greetings color={red} text='Hello' />
</div>
Copy the code
In the child component, props is available under this.props.
Child components:
const Greetings = React.createClass({
render () {
const {color, text} = this.props
const divStyle = {padding: 10, backgroundColor: 'black'}
const headingStyle = {color: color}
return (
<div style={divStyle}>
<h1 style={headingStyle}>{text}</h1>
</div>
)
}
})
Copy the code
7) State
So far, we’ve only talked about static components and static data passing to the component tree. Typically, you want to create a stateful component where the state changes over time. Let’s consider a component that you can enter into this article and display below.
const InputBox = React.createClass({
getInitialState () {
return {
text: ''
}
},
changeText (event) {
this.setState({text: event.target.value})
},
render () {
return (
<div>
<input type='text' onChange={this.changeText} placeholder='text' value={this.state.text} />
<span>{this.state.text}</span>
</div>
)
}
})
Copy the code
At the beginning, you set the default state of the component. In this case, we want to have an empty text value. To do this, you use the component method getInitialState (), which must return a component’s state object.
To update the status, the event handler changeText () is assigned to the onChange event. To update the state React expects you to use the built-in setState () method.
The component state will be re-rendered once it has been updated. The setState () call is needed to notify React about pending state changes so that it can apply the changes. If something changes, there is no loop of any kind.
You need to remember that setState () is asynchronous. The results will not take effect immediately. The following example shows both the bad and good ways to access state immediately after applying changes:
// Error: //... SomeFunction (value) {this.setstate ({someValue: value}) // May not change console.log('New value: ', this.state.someValue) } // ...Copy the code
// Good: //... SomeFunction (value) {this.setState({someValue: value}, () => {// do something with the New state console.log('New value: ', this.state.someValue) }) } // ...Copy the code
Okay, but what if you need to propagate state changes to the parent component?
8) Incidents rise
Let’s say we have a Parent component that needs to get data from the InputBox child of the previous example.
const Parent = React.createClass({
gimmeThatState (textFromInput) {
console.log(textFromInput)
// or this.setState({text: textFromInput})
},
render () {
<InputBox pushChangesUp={this.gimmeThatState} />
}
})
Copy the code
The Parent component passes a function (as a prop) so that InputBox can be used to push some data.
The changed InputBox might look something like this:
const InputBox = React.createClass({
propTypes: {
pushChangesUp: React.PropTypes.func.isRequired
},
getInitialState () {
return {
text: ''
}
},
changeText (event) {
this.setState({text: event.target.value})
},
pushChangesUp () {
this.props.pushChangesUp(this.state.text)
}
render () {
return (
<div>
<input type='text' onChange={this.changeText} placeholder='text' value={this.state.text} />
<span>{this.state.text}</span>
<button onClick={this.pushChangesUp}>Push changes up</button>
</div>
)
}
})
Copy the code
The first thing you’ll see is a propTypes property at the beginning of the component declaration. It is used to validate properties. To me, it acts like an interface in OOP. By looking at propTypes I immediately know what properties I need to provide to the component to make it work.
Next, the local pushChangesUp () event handler is assigned to the onClick event on the button. When clicked, this function uses pushChangesUp () to push data from props.
The Parent component can now save data to its own state and pass it to different components.
9) How does rendering work
Each call to the setState () method notifies React that the state has changed. React then calls the Render () method to update the in-memory component presentation content (the Virtual DOM) and compare it to what is rendered in the browser. React updates the DOM as little as possible if there are changes.
The child components know they need to rerender because their props have changed.
I often use the diff mechanism on Git as an analogy to this concept. With a snapshot of the two component trees, React compares and then simply exchanges what needs to be exchanged.
I was looking for a clever diagram to describe the render flow, but couldn’t find it. But you can read more about this concept here.
10) Mix is key
A parent component that has state is often called a container component. They are responsible for state management and (as strange as it sounds) rendering child components. The child component is used to trigger event handlers passed from the parent class (such as the InputBox component in the previous example) and display data. The subcomponents responsible for displaying data are called presentation components.
Container components are typically responsible for fetching data, API calls (see componentDidMount() lifecycle methods), and so on. You should keep it in one place to avoid showing side effects in components. Container components should be as oblivious as possible, rather than displaying data.
This separation of concerns and terminology is popularized by Dan Abramov, author of Redux. You can read more in his article.
You can see that it all fits together. When each component part follows a single responsibility, it can be reused with other components.
The biggest challenge is figuring out how to divide these responsibilities and where to place the status. If you want to know more about this topic, search for “Thinking in React” articles.
11) Keep state small
In my training, there are often exercises that involve some kind of list filtering. Suppose you have a list of Todos:
const initialState = [ {id: 1, text: 'laundry'}, {id: 2, text: 'shopping'} // ... ] const List = React.createClass({ getInitialState () { return { todos: initialState } }, render () { return ( <div> <ul> {this.state.todos.map(todo => <li key={todo.id}>{todo.text}</li>)} </ul> </div> ) } })Copy the code
Now you will add a search box. 50% of people will adopt this method of variation:
const initialState = [ {id: 1, text: 'laundry'}, {id: 2, text: 'shopping'} // ... ] const List = React.createClass({ getInitialState () { return { todos: initialState, filteredTodos: null } }, search (searchText) { const filteredTodos = this.state.todos(todo => todo.text.indexOf(searchText) > 0) this.setState({filteredTodos: filteredTodos}) }, render () { const {filteredTodos, todos} = this.state // get todos from state const list = filteredTodos === null ? todos : filteredTodos // if there are filtered todos use them return ( <div> <SearchBox onChange={this.search} /> <ul> {list.map(todo => <li key={todo.id}>{todo.text}</li>)} </ul> </div> ) } })Copy the code
You can see here is the state of repetition, two sources of truth and an introduction to spaghetti code. Imagine that you want to update a to-do list and enable search filters.
What’s better? Keep the state as small as possible. If something can be calculated on the fly, then it should be.
There’s a better way to do this:
const initialState = [ {id: 1, text: 'laundry'}, {id: 2, text: 'shopping'} // ... ] const List = React.createClass({ getInitialState () { return { todos: initialState, searchText: null } }, search (searchText) { this.setState({searchText: searchText}) }, filter (todos) { if (! this.state.searchText) { return todos } return todos.filter(todo => todo.text.indexOf(this.state.searchText) > 0) }, render () { return ( <div> <SearchBox onChange={this.search} /> <ul> {this.filter(list).map(todo => <li key={todo.id}>{todo.text}</li>)} </ul> </div> ) } })Copy the code
Here you only keep searchText to filter the list before rendering. A cleaner approach, a smaller state and no repetition.
12) Conditional rendering
In the previous example, the list was rendered according to some conditional logic. If some criteria are met, you usually need to render part of the tag, and if not, you need to render another part. There are several ways to do this in React.
Ternary operator
In JSX, you can use ternary operators to handle conditional rendering:
React.createClass({
getInitialState () {
return {
hideTodos: true
}
},
render () {
return (
<div>
{
hideTodos ? 'Sorry there is no data' : <TodoList />
}
</div>
)
}
})
Copy the code
Possible changes:
-
A ternary operator other than a return expression
-
An if/else block outside of the return expression
Helper method
React.createClass({
getInitialState () {
return {
hideTodos: true
}
},
renderTodos () {
if (this.state.hideTodos) {
return 'Sorry there is no data'
}
return <TodoList />
}
render () {
return (
<div>
{
this.renderTodos()
}
</div>
)
}
})
Copy the code
This is a useful method, but when the component is larger, you need to jump up and down between the helper method and the Render () method.
A component
Because a feature switch works so well, this is probably the simplest way to do it. ?????
React.createClass({ getInitialState () { return { hideTodos: true } }, render () { return ( <div> <HideIf condition={this.state.hideTodos}> <TodoList /> </HideIf> </div> ) } }) const HideIf = React.createClass({ render () { if (this.props.condition) { return <span>'Sorry there is no data'</span> } return this.props.children // children is what's inside <HideIf> element } })Copy the code
13) You don’t need Flux
Probably the most common misconception among React newcomers is that you need to use in conjunction with Redux or some other Flux implementation.
Redux is good. It is the most popular Flux implementation, and offers a great deal of functionality and a concise, practical, testable method, thanks to the great tutorial. But [you probably don’t need it] (medium.com/dan_abramo…
If you’re learning React, if your application is small, if you don’t need global state, or you don’t have any issues that need to track changes in application state — don’t use it. If you want to know more, read here.
conclusion
So, here’s a list of React concepts THAT I talk to people about the most. I highly recommend reading React Docs as it is the best source of knowledge from start to finish. I also recommend watching early React videos (from 2013 to 2014) that describe the problems Facebook was trying to solve when creating React. It will help you realize if you are having similar problems React will help you or should you stick with some other techniques.