Update: This article is now part of my book “React.js Beyond The Basics”.
更新:本文現在是我的書《超越基礎的React.js》的一部分。
Read the updated version of this content and more about React at jscomplete.com/react-beyond-basics.
在jscomplete.com/react-beyond-basics中閱讀此內容的更新版本以及有關React的更多信息。
This article is not going to cover what React is or why you should learn it. Instead, this is a practical introduction to the fundamentals of React.js for those who are already familiar with JavaScript and know the basics of the DOM API.
本文不會介紹什么是React或為什么要學習它 。 相反,這是對那些已經熟悉JavaScript并了解DOM API基礎知識的人的React.js基礎知識的實用介紹。
All code examples below are labeled for reference. They are purely intended to provide examples of concepts. Most of them can be written in a much better way.
以下所有代碼示例均已標記為參考。 它們純粹是為了提供概念的示例。 它們中的大多數可以用更好的方式編寫。
基本原理1:React完全涉及組件 (Fundamental 1: React is all about components)
React is designed around the concept of reusable components. You define small components and you put them together to form bigger components.
React是圍繞可重用??組件的概念設計的。 您定義小組件,然后將它們放在一起以形成更大的組件。
All components small or big are reusable, even across different projects.
所有大小的組件都可以重用,即使在不同的項目中也可以重用。
A React component — in its simplest form — is a plain-old JavaScript function:
一個最簡單的形式的React組件是一個普通JavaScript函數:
// Example 1
// https://jscomplete.com/repl?j=Sy3QAdKHW
function Button (props) {// Returns a DOM element here. For example:return <button type="submit">{props.label}</button>;
}
// To render the Button component to the browser
ReactDOM.render(<Button label="Save" />, mountNode)
The curly braces used for the button label are explained below. Don’t worry about them now. ReactDOM
will also be explained later, but if you want to test this example and all upcoming code examples, the above render
function is what you need.
下面說明用于按鈕標簽的花括號。 現在不用擔心它們。 ReactDOM
也將在后面解釋,但是如果您想測試該示例以及所有即將發布的代碼示例,則需要上面的render
函數。
The second argument to ReactDOM.render
is the destination DOM element which React is going to take over and control. In the jsComplete React Playground, you can just use the special variable mountNode
.
ReactDOM.render
的第二個參數是React將要接管并控制的目標DOM元素。 在jsComplete React Playground中 ,您可以僅使用特殊變量mountNode
。
JavaScript REPL and Playground for React.jsTest modern JavaScript and React.js code in the browser without any configurationsjscomplete.com/react
適用于React.jsJavaScript REPL和Playground 無需任何配置即可在瀏覽器中測試現代JavaScript和React.js代碼 jscomplete.com/react
Note the following about Example 1:
請注意以下有關示例1的內容:
- The component name starts with a capital letter. This is required since we will be dealing with a mix of HTML elements and React elements. Lowercase names are reserved for HTML elements. In fact, go ahead and try to name the React component just “button” and see how ReactDOM will ignore the function and renders a regular empty HTML button. 組件名稱以大寫字母開頭。 這是必需的,因為我們將處理HTML元素和React元素的混合。 小寫名稱保留給HTML元素。 實際上,繼續嘗試將React組件命名為“ button”,看看ReactDOM如何忽略該函數并呈現常規的空HTML按鈕。
Every component receives a list of attributes, just like HTML elements. In React, this list is called props. With a function component, you can name it anything though.
每個組件都會收到一個屬性列表,就像HTML元素一樣。 在React中,此列表稱為props 。 使用功能組件,您可以為其命名。
We weirdly wrote what looks like HTML in the returned output of the
Button
function component above. This is neither JavaScript nor HTML, and it’s not even React.js. But, it’s so popular that it became the default in React applications. It’s called JSX and it’s a JavaScript extension. JSX is also a compromise! Go ahead and try and return any other HTML element inside the function above and see how they are all supported (for example, return a text input element).我們在上面的
Button
函數組件的返回輸出中怪異地寫了HTML。 這既不是JavaScript也不是HTML,甚至都不是React.js。 但是,它是如此流行,以至于成為了React應用程序的默認設置。 它稱為JSX ,是JavaScript擴展。 JSX也是一種折衷方案 ! 繼續嘗試并返回上面函數內的任何其他HTML元素,并查看如何全部支持它們(例如,返回文本輸入元素)。
基本原理2:JSX的作用是什么? (Fundamental 2: What the flux is JSX?)
Example 1 above can be written in pure React.js without JSX as follows:
上面的示例1可以用不帶JSX的純React.js編寫,如下所示:
// Example 2 - React component without JSX
// https://jscomplete.com/repl?j=HyiEwoYB-
function Button (props) {return React.createElement("button",{ type: "submit" },props.label);
}
// To use Button, you would do something like
ReactDOM.render(React.createElement(Button, { label: "Save" }),mountNode
);
The createElement
function is the main function in the React top-level API. It’s 1 of a total of 8 things in that level that you need to learn. That’s how small the React API is.
createElement
函數是React頂級API中的主要函數。 這是您需要學習的8項內容中的1項。 那就是React API的大小。
Much like the DOM itself having a document.createElement
function to create an element specified by a tag name, React’s createElement
function is a higher-level function that can do what document.createElement
does, but it can also be used to create an element to represent a React component. We did the latter when we used the Button
component in Example 2 above.
就像DOM本身具有document.createElement
函數來創建由標簽名指定的元素一樣,React的createElement
函數是一個高級函數,可以完成document.createElement
工作,但是它也可以用于創建元素來代表一個React組件 當我們在上面的示例2中使用Button
組件時,我們執行了后者。
Unlike document.createElement
, React’s createElement
accepts a dynamic number of arguments after the second one to represent the children of the created element. So createElement
actually creates a tree.
與document.createElement
不同,React的createElement
在第二個參數之后接受動態數量的參數來表示所創建元素的子代 。 因此createElement
實際上會創建一棵樹 。
Here’s an example of that:
這是一個例子:
// Example 3 - React’s createElement API
// https://jscomplete.com/repl?j=r1GNoiFBb
const InputForm = React.createElement("form",{ target: "_blank", action: "https://google.com/search" },React.createElement("div", null, "Enter input and click Search"),React.createElement("input", { name: "q", className: "input" }),React.createElement(Button, { label: "Search" })
);
// InputForm uses the Button component, so we need that too:
function Button (props) {return React.createElement("button",{ type: "submit" },props.label);
}
// Then we can use InputForm directly with .render
ReactDOM.render(InputForm, mountNode);
Note a few things about the example above:
請注意有關上面的示例的一些注意事項:
InputForm
is not a React component; it’s just a React element. This is why we used it directly in theReactDOM.render
call and not with<InputForm
/>.InputForm
不是React組件; 它只是一個React 元素 。 這就是為什么我們直接在ReactDOM.render
調用中而不是在<InputForm
/>中使用它的原因。The
React.createElement
function accepted multiple arguments after the first two. Its list of arguments starting from the 3rd one comprises the list of children for the created element.React.createElement
函數在前兩個之后接受多個參數。 它的參數列表從第3個開始,包括所創建元素的子級列表。We were able to nest
React.createElement
calls because it’s all JavaScript.我們能夠嵌套
React.createElement
調用,因為它們都是JavaScript。The second argument to
React.createElement
can be null or an empty object when no attributes or props are needed for the element.當元素不需要屬性或道具時,
React.createElement
的第二個參數可以為null或為空對象。- We can mix HTML element with React elements. 我們可以將HTML元素與React元素混合使用。
React’s API tries to be as close to the DOM API as possible, that’s why we use
className
instead ofclass
for the input element. Secretly, we all wish the React’s API would become part of the DOM API itself. Because, you know, it’s much much better.React的API嘗試盡可能地接近DOM API,這就是為什么我們使用
className
而不是class
作為輸入元素的原因。 秘密地,我們都希望React的API成為DOM API本身的一部分。 因為,這要好得多。
The code above is what the browser understands when you include the React library. The browser does not deal with any JSX business. However, we humans like to see and work with HTML instead of these createElement
calls (imagine building a website with just document.createElement
, which you can!). This is why the JSX compromise exists. Instead of writing the form above with React.createElement
calls, we can write it with a syntax very similar to HTML:
上面的代碼是您包含React庫時瀏覽器可以理解的內容。 該瀏覽器不處理任何JSX業務。 但是,我們人類喜歡查看并使用HTML而不是使用這些createElement
調用(想象只使用document.createElement
構建網站!)。 這就是為什么存在JSX折衷的原因。 不用使用React.createElement
調用編寫上面的表單,我們可以使用非常類似于HTML的語法來編寫它:
// Example 4 - JSX (compare with Example 3)
// https://jscomplete.com/repl?j=SJWy3otHW
const InputForm =<form target="_blank" action="https://google.com/search"><div>Enter input and click Search</div><input name="q" className="input" /><Button label="Search" /></form>;
// InputForm "still" uses the Button component, so we need that too.
// Either JSX or normal form would do
function Button (props) {// Returns a DOM element here. For example:return <button type="submit">{props.label}</button>;
}
// Then we can use InputForm directly with .render
ReactDOM.render(InputForm, mountNode);
Note a few things about the above:
請注意上述幾點:
It’s not HTML. For example, we’re still doing
className
instead ofclass
.不是HTML。 例如,我們仍在使用
className
代替class
。- We’re still considering what looks like HTML above as JavaScript. See how I added a semicolon at the end. 我們仍在考慮上面看起來像HTMLJavaScript。 看看我如何在末尾添加分號。
What we wrote above (Example 4) is JSX. Yet, what we took to the browser is the compiled version of it (Example 3). To make that happen, we need to use a pre-processor to convert the JSX version into the React.createElement
version.
上面我們寫的(示例4)是JSX。 但是,我們帶給瀏覽器的是它的編譯版本(示例3)。 為此,我們需要使用預處理器將JSX版本轉換為React.createElement
版本。
That is JSX. It’s a compromise that allows us to write our React components in a syntax similar to HTML, which is a pretty good deal.
那就是JSX。 這是一個折衷方案,使我們能夠以類似于HTML的語法編寫React組件,這是非常不錯的。
The word “Flux” in the header above was chosen to rhyme, but it’s also the name of a very popular application architecture popularized by Facebook. The most famous implementation of which is Redux. Flux fits the React reactive pattern perfectly.
上面標題中的“ Flux”一詞被選為押韻,但這也是Facebook流行的非常流行的應用程序體系結構的名稱。 其中最著名的實現是Redux。 助焊劑非常適合ReactReact模式。
JSX, by the way, can be used on its own. It’s not a React-only thing.
順便說一下,JSX可以單獨使用。 這不是只做React的事情。
基本原理3:您可以在JSX中的任何位置使用JavaScript表達式 (Fundamental 3: You can use JavaScript expressions anywhere in JSX)
Inside a JSX section, you can use any JavaScript expression within a pair of curly braces.
在JSX部分中,您可以在一對花括號內使用任何JavaScript表達式。
// To use it:ReactDOM.render(<RandomValue />, mountNode);// Example 5 - Using JavaScript expressions in JSX
// https://jscomplete.com/repl?j=SkNN3oYSW
const RandomValue = () => <div>{ Math.floor(Math.random() * 100) }</div>;
// To use it:
ReactDOM.render(<RandomValue />, mountNode);
Any JavaScript expression can go inside those curly braces. This is equivalent to the ${}
interpolation syntax in JavaScript template literals.
任何JavaScript表達式都可以放在這些花括號內。 這等效于JavaScript 模板文字中的${}
插值語法。
This is the only constraint inside JSX: only expressions. So, for example, you can’t use a regular if
statement, but a ternary expression is ok.
這是JSX內部的唯一約束:僅表達式。 因此,例如,您不能使用正則if
語句,但可以使用三元表達式。
JavaScript variables are also expressions, so when the component receives a list of props (the RandomValue
component didn’t, props
are optional), you can use these props inside curly braces. We did this in the Button
component above (Example 1).
JavaScript變量也是表達式,因此當組件接收到一系列道具時( RandomValue
組件沒有, props
是可選的),您可以在花括號內使用這些道具。 我們在上面的Button
組件中進行了此操作(示例1)。
JavaScript objects are also expressions. Sometimes we use a JavaScript object inside curly braces, which makes it look like double curly braces, but it’s really just an object inside curly braces. One use case of that is to pass a CSS style object to the special style
attribute in React:
JavaScript對象也是表達式。 有時我們在花括號內使用JavaScript對象,這使其看起來像雙花括號,但實際上只是花括號內的對象。 一個用例是將CSS樣式對象傳遞給React中的特殊style
屬性:
// Example 6 - An object passed to the special React style prop
// https://jscomplete.com/repl?j=S1Kw2sFHb
const ErrorDisplay = ({message}) =><div style={ { color: 'red', backgroundColor: 'yellow' } }>{message}</div>;
// Use it:
ReactDOM.render(<ErrorDisplay message="These aren't the droids you're looking for" />,mountNode
);
Note how I destructured only the message out of the props argument. Also note how the style
attribute above is a special one (again, it’s not HTML, it’s closer to the DOM API). We use an object as the value of the style
attribute. That object defines the styles as if we’re doing so with JavaScript (because we are).
請注意我是如何從props參數中解構出消息的。 還要注意上面的style
屬性是如何特殊的(同樣,它不是HTML,它更接近DOM API)。 我們使用一個對象作為style
屬性的值。 該對象定義樣式,就像我們使用JavaScript一樣(因為是)。
You can even use a React element inside JSX, because that too is an expression. Remember, a React element is essentially a function call:
您甚至可以在JSX中使用React元素,因為那也是一個表達式。 記住,React元素本質上是一個函數調用:
// Example 7 - Using a React element within {}
// https://jscomplete.com/repl?j=SkTLpjYr-
const MaybeError = ({errorMessage}) =><div>{errorMessage && <ErrorDisplay message={errorMessage} />}</div>;// The MaybeError component uses the ErrorDisplay component:
const ErrorDisplay = ({message}) =><div style={ { color: 'red', backgroundColor: 'yellow' } }>{message}</div>;
// Now we can use the MaybeError component:
ReactDOM.render(<MaybeErrorerrorMessage={Math.random() > 0.5 ? 'Not good' : ''}/>,mountNode
);
The MaybeError
component above would only display the ErrorDisplay
component if there is an errorMessage
string passed to it and an empty div
. React considers {true}
, {false}
, {undefined}
, and {null}
to be valid element children, which do not render anything.
如果傳遞了errorMessage
字符串和一個空div
,則上面的MaybeError
組件將僅顯示ErrorDisplay
組件。 React認為{true}
, {false}
, {undefined}
和{null}
是有效的元素子元素,它們不會呈現任何內容。
You can also use all of JavaScript functional methods on collections (map
, reduce
, filter
, concat
, and so on) inside JSX. Again, because they return expressions:
您還可以在JSX內的集合( map
, reduce
, filter
, concat
等)上使用所有JavaScript功能方法。 同樣,因為它們返回表達式:
// Example 8 - Using an array map inside {}
// https://jscomplete.com/repl?j=SJ29aiYH-
const Doubler = ({value=[1, 2, 3]}) =><div>{value.map(e => e * 2)}</div>;
// Use it
ReactDOM.render(<Doubler />, mountNode);
Note how I gave the value
prop a default value above, because it’s all just Javascript. Note also that I outputted an array expression inside the div
. React is okay with that; It will place every doubled value in a text node.
請注意,我是如何給value
prop設置上面的默認值的,因為它們全都是Javascript。 還要注意,我在div
輸出了一個數組表達式。 React對此還可以; 它將每個雙精度值放置在文本節點中。
基礎知識4:您可以使用JavaScript類編寫React組件 (Fundamental 4: You can write React components with JavaScript classes)
Simple function components are great for simple needs, but sometimes we need more. React supports creating components through the JavaScript class syntax as well. Here’s the Button
component (in Example 1) written with the class syntax:
簡單的功能組件非常適合簡單的需求,但有時我們需要更多。 React也支持通過JavaScript類語法創建組件。 這是用類語法編寫的Button
組件(在示例1中):
// Example 9 - Creating components using JavaScript classes
// https://jscomplete.com/repl?j=ryjk0iKHb
class Button extends React.Component {render() {return <button>{this.props.label}</button>;}
}
// Use it (same syntax)
ReactDOM.render(<Button label="Save" />, mountNode);
The class syntax is simple. Define a class that extends React.Component
(another top-level React API thing that you need to learn). The class defines a single instance function render()
, and that render function returns the virtual DOM element. Every time we use the Button
class-based component above (for example, by doing <Button ...
/>), React will instantiate an object from this class-based component and use that object to render a DOM element in the DOM tree.
類語法很簡單。 定義一個擴展React.Component
的類(您需要學習的另一個頂級React API東西)。 該類定義單個實例函數render()
,并且該渲染函數返回虛擬DOM元素。 每次我們使用上面的基于Button
類的組件時(例如,通過執行<Button ...
/>),React將從該基于類的組件中實例化一個對象,并使用該對象在DOM樹中呈現DOM元素。 。
This is the reason why we used this.props.label
inside the JSX in the rendered output above. Because every element rendered through a class component gets a special instance property called props
that holds all values passed to that element when it was created.
這就是為什么我們在上面呈現的輸出中的JSX中使用this.props.label
的原因。 因為通過類組件呈現的每個元素都會獲得一個稱為props
的特殊實例屬性,該屬性保存創建時傳遞給該元素的所有值。
Since we have an instance associated with a single use of the component, we can customize that instance as we wish. We can, for example, customize it after it gets constructed by using the regular JavaScript constructor
function:
由于我們有一個與組件的單一使用相關聯的實例,因此我們可以根據需要自定義該實例。 例如,我們可以在使用常規JavaScript constructor
函數構造后對其進行自定義:
// Example 10 - Customizing a component instance
// https://jscomplete.com/repl?j=rko7RsKS-
class Button extends React.Component {constructor(props) {super(props);this.id = Date.now();}render() {return <button id={this.id}>{this.props.label}</button>;}
}
// Use it
ReactDOM.render(<Button label="Save" />, mountNode);
We can also define class functions and use them anywhere we wish, including inside the returned JSX output:
我們還可以定義類函數,并在希望的任何地方使用它們,包括在返回的JSX輸出內部:
// Example 11 — Using class properties
// https://jscomplete.com/repl?j=H1YDCoFSb
class Button extends React.Component {clickCounter = 0;handleClick = () => {console.log(`Clicked: ${++this.clickCounter}`);};render() {return (<button id={this.id} onClick={this.handleClick}>{this.props.label}</button>);}
}
// Use it
ReactDOM.render(<Button label="Save" />, mountNode);
Note a few things about Example 11 above:
請注意上述示例11的一些注意事項:
The
handleClick
function is written using the new proposed class-field syntax in JavaScript. This is still at stage-2, but for many reasons it’s the best option to access the component mounted instance (thanks to arrow functions). But, you need to use a compiler like Babel configured to understand stage-2 (or the class-field syntax) to get the code above to work. The jsComplete REPL has that pre-configured.handleClick
函數是使用JavaScript中新提議的類字段語法編寫的。 這仍然處于階段2,但是由于許多原因,這是訪問組件安裝的實例的最佳選擇(由于使用了箭頭功能)。 但是,您需要使用像Babel這樣的編譯器,將其配置為了解Stage-2(或類字段語法),才能使上面的代碼正常工作。 jsComplete REPL已預先配置。We’ve also defined the
clickCounter
instance variables using the same class-field syntax. This allows us to skip using a class constructor call altogether.我們還使用相同的類字段語法定義了
clickCounter
實例變量。 這使我們可以完全跳過使用類構造函數的調用。When we specified the
handleClick
function as the value of the specialonClick
React attribute, we did not call it. We passed in the reference to thehandleClick
function. Calling the function on that level is one of the most common mistakes when working with React.當我們將
handleClick
函數指定為特殊onClick
React屬性的值時,我們沒有調用它。 我們將引用傳遞給handleClick
函數。 在使用React時,在該級別上調用函數是最常見的錯誤之一。
// Wrong:
onClick={this.handleClick()}
// Right:
onClick={this.handleClick}
基礎5:React中的事件:兩個重要區別 (Fundamental 5: Events in React: Two Important Differences)
When handling events inside React elements, there are two very important differences from the way we do so with the DOM API:
在React元素內部處理事件時,與我們使用DOM API的方式有兩個非常重要的區別:
All React elements attributes (events included) are named using camelCase, rather than lowercase. It’s
onClick
, notonclick
.所有React元素屬性(包括事件)都使用camelCase命名,而不是小寫 。 它是
onClick
,而不是onclick
。We pass an actual JavaScript function reference as the event handler, rather than a string. It’s
onClick={handleClick}
, notonClick="handleClick"
.我們傳遞實際JavaScript函數引用作為事件處理程序,而不是字符串。 它是
onClick={ handleClick }
,而不是onClick=" handleClick"
。
React wraps the DOM event object with an object of its own to optimize the performance of events handling. But inside an event handler, we can still access all methods available on the DOM event object. React passes that wrapped event object to every handle call. For example, to prevent a form from the default submission action, you can do:
React用自己的對象包裝DOM事件對象,以優化事件處理的性能。 但是在事件處理程序內部,我們仍然可以訪問DOM事件對象上可用的所有方法。 React將包裝的事件對象傳遞給每個句柄調用。 例如,要阻止表單執行默認的提交操作,可以執行以下操作:
// Example 12 - Working with wrapped events
// https://jscomplete.com/repl?j=HkIhRoKBb
class Form extends React.Component {handleSubmit = (event) => {event.preventDefault();console.log('Form submitted');};render() {return (<form onSubmit={this.handleSubmit}><button type="submit">Submit</button></form>);}
}
// Use it
ReactDOM.render(<Form />, mountNode);
基本原理6:每個React組件都有一個故事 (Fundamental 6: Every React component has a story)
The following applies to the class component only (those that extend React.Component
). Function components have a slightly different story.
以下內容僅適用于類組件(那些擴展了React.Component
)。 功能組件的故事略有不同。
- First, we define a template for React to create elements from the component. 首先,我們為React定義一個模板,以從組件創建元素。
Then, we instruct React to use it somewhere. For example, inside a
render
call of another component, or withReactDOM.render
.然后,我們指示React在某處使用它。 例如,在另一個組件的
render
調用中,或在ReactDOM.render
。Then, React instantiates an element and gives it a set of props that we can access with
this.props
. Those props are exactly what we passed in step 2 above.然后,React實例化一個元素,并為其提供一組道具 ,我們可以使用
this.props
訪問。 這些道具正是我們在上面的步驟2中通過的。Since it’s all JavaScript, the
constructor
method will be called (if defined). This is the first of what we call: component lifecycle methods.由于全部使用JavaScript,因此將調用
constructor
方法(如果已定義)。 這是我們所謂的第一個: 組件生命周期方法 。- React then computes the output of the render method (the virtual DOM node). 然后,React計算render方法(虛擬DOM節點)的輸出。
Since this is the first time React is rendering the element, React will communicate with the browser (on our behalf, using the DOM API) to display the element there. This process is commonly known as mounting.
由于這是React首次渲染元素,因此React將與瀏覽器通信(代表我們,使用DOM API)以在其中顯示元素。 此過程通常稱為安裝 。
React then invokes another lifecycle method, called
componentDidMount
. We can use this method to, for example, do something on the DOM that we now know exists in the browser. Prior to this lifecycle method, the DOM we work with was all virtual.然后,React調用另一個生命周期方法,稱為
componentDidMount
。 例如,我們可以使用此方法在DOM上執行某些我們現在知道存在于瀏覽器中的操作。 在使用這種生命周期方法之前,我們使用的DOM都是虛擬的。Some components stories end here. Other components get unmounted from the browser DOM for various reasons. Right before the latter happens, React invokes another lifecycle method,
componentWillUnmount
.一些組件的故事到此結束。 由于各種原因,其他組件也會從瀏覽器DOM中卸載。 就在后者發生之前,React調用了另一個生命周期方法
componentWillUnmount
。The state of any mounted element might change. The parent of that element might re-render. In either case, the mounted element might receive a different set of props. React magic happens here and we actually start needing React at this point! Prior to this point, we did not need React at all, honestly.
任何已安裝元素的狀態都可能更改。 該元素的父元素可能會重新呈現。 在任何一種情況下,已安裝的元素可能會接收到不同的道具集。 React魔術在這里發生,我們此時實際上開始需要 React! 在此之前,老實說,我們根本不需要React。
The story of this component continues, but before it does, we need to understand this state thing that I speak of.
此組件的故事還在繼續,但它之前,我們需要了解這個國家的事情,我說的。
基本原理7:React組件可以具有私有狀態 (Fundamental 7: React components can have a private state)
The following is also only applicable to class components. Did I mention that some people call presentational-only components dumb?
以下內容也僅適用于類組件。 我是否提到有些人稱僅表示組件愚蠢 ?
The state
property is a special one in any React class component. React monitors every component state for changes. But for React to do so efficiently, we have to change the state field through another React API thing that we need to learn, this.setState
:
state
屬性在任何React類組件中都是一個特殊的屬性。 React監視每個組件狀態的更改。 但是為了使React有效地做到這一點,我們必須通過需要學習的另一個React API事物this.setState
來更改狀態字段:
// Example 13 - the setState API
// https://jscomplete.com/repl?j=H1fek2KH-
class CounterButton extends React.Component {state = {clickCounter: 0,currentTimestamp: new Date(),};handleClick = () => {this.setState((prevState) => {return { clickCounter: prevState.clickCounter + 1 };});};componentDidMount() {setInterval(() => {this.setState({ currentTimestamp: new Date() })}, 1000);}render() {return (<div><button onClick={this.handleClick}>Click</button><p>Clicked: {this.state.clickCounter}</p><p>Time: {this.state.currentTimestamp.toLocaleString()}</p></div>);}
}
// Use it
ReactDOM.render(<CounterButton />, mountNode);
This is the most important example to understand. It will basically complete your fundamental knowledge of the React way. After this example, there are a few other small things that you need to learn, but it’s mostly you and your JavaScript skills from that point.
這是最重要的例子。 它將基本完成您對React方法的基礎知識。 在此示例之后,還需要學習其他一些小知識,但是從那時起,主要是您和您JavaScript技能。
Let’s walk through Example 13, starting with class fields. It has two of them. The special state
field is initialized with an object that holds a clickCounter
that starts with 0
, and a currentTimestamp
that starts with new Date()
.
讓我們從類字段開始講解示例13。 它有兩個。 特殊state
字段由一個對象初始化,該對象包含一個以0
開頭的clickCounter
和一個以new Date()
開頭的currentTimestamp
。
The second class field is a handleClick
function, which we passed to the onClick
event for the button element inside the render method. The handleClick
method modifies this component instance state using setState
. Take notice of that.
第二個類字段是handleClick
函數,我們將其傳遞給render方法內button元素的onClick
事件。 handleClick
方法使用setState
修改此組件實例狀態。 注意這一點。
The other place we’re modifying the state is inside an interval timer that we started inside the componentDidMount
lifecycle method. It ticks every second and executes another call to this.setState
.
我們要修改狀態的另一個地方是在componentDidMount
生命周期方法內啟動的間隔計時器內。 它每秒滴答this.setState
并執行對this.setState
另一個調用。
In the render method, we used the two properties we have on the state with a normal read syntax. There is no special API for that.
在render方法中,我們使用正常讀取語法在狀態上使用了兩個屬性。 對此沒有特殊的API。
Now, notice that we updated the state using two different ways:
現在,請注意,我們使用兩種不同的方式來更新狀態:
By passing a function that returned an object. We did that inside the
handleClick
function.通過傳遞返回對象的函數。 我們在
handleClick
函數中進行了此操作。- By passing a regular object. We did that inside the interval callback. 通過傳遞常規對象。 我們是在時間間隔回調中完成的。
Both ways are acceptable, but the first one is preferred when you read and write to the state at the same time (which we do). Inside the interval callback, we’re only writing to the state and not reading it. When in doubt, always use the first function-as-argument syntax. It’s safer with race conditions because setState
should always be treated as an asynchronous method.
兩種方法都可以接受,但是當您同時讀取和寫入狀態時(我們這樣做),首選第一種方法。 在時間間隔回調中,我們僅寫入狀態,而不讀取狀態。 如有疑問,請始終使用第一個函數自變量語法。 在競爭條件下更安全,因為setState
應該始終被視為異步方法。
How do we update the state? We return an object with the new value of what we want to update. Notice how in both calls to setState
, we’re only passing one property from the state field and not both. This is completely okay because setState
actually merges what you pass it (the returned value of the function argument) with the existing state. So, not specifying a property while calling setState
means that we wish to not change that property (but not delete it).
我們如何更新狀態? 我們返回一個具有我們要更新的新值的對象。 請注意,在兩次調用setState
,我們僅從狀態字段傳遞一個屬性,而不是兩者。 這完全可以,因為setState
實際上將您傳遞的內容(函數參數的返回值)與現有狀態合并。 因此,在調用setState
未指定屬性意味著我們希望不更改該屬性(但不要刪除它)。
基礎8:React會做出React (Fundamental 8: React will react)
React gets its name from the fact that it reacts to state changes (although not reactively, but on a schedule). There was a joke that React should have been named Schedule!
React之所以得名,是因為它對狀態更改做出了React (盡管不是被動的,而是按計劃進行的)。 開個玩笑說React應該被命名為Schedule !
However, what we witness with the naked eye when the state of any component gets updated is that React reacts to that update and automatically reflects the update in the browser DOM (if needed).
但是,當任何組件的狀態被更新時,我們用肉眼看到的是,React對更新進行React,并自動在瀏覽器DOM中反映該更新(如果需要)。
Think of the render function’s input as both:
將渲染函數的輸入都視為:
- The props that get passed by the parent 父母通過的道具
- The internal private state that can be updated anytime 可以隨時更新的內部私有狀態
When the input of the render function changes, its output might change.
當渲染函數的輸入更改時,其輸出可能更改。
React keeps a record of the history of renders and when it sees that one render is different than the previous one, it’ll compute the difference between them and efficiently translate it into actual DOM operations that get executed in the DOM.
React記錄了渲染的歷史記錄,當發現一個渲染與上一個渲染不同時,React將計算它們之間的差異,并將其有效地轉換為在DOM中執行的實際DOM操作。
基礎知識9:React是您的代理 (Fundamental 9: React is your agent)
You can think of React as the agent we hired to communicate with the browser. Take the current timestamp display above as an example. Instead of us manually going to the browser and invoking DOM API operations to find and update the p#timestamp
element every second, we just changed a property on the state of the component and React did its job of communicating with the browser on our behalf. I believe this is the true reason why React is popular. We hate talking to Mr. Browser (and the so many dialects of the DOM language that it speaks) and React volunteered to do all the talking for us, for free.
您可以將React視為我們雇用的與瀏覽器進行通信的代理。 以上面的當前時間戳顯示為例。 代替我們手動去瀏覽器并每秒調用DOM API操作來查找和更新p#timestamp
元素,我們只是更改了組件狀態的屬性,而React代表我們與瀏覽器進行通信。 我相信這是React受歡迎的真正原因。 我們討厭與Browser先生(以及它所講的DOM語言的眾多方言)交談,而React自愿為我們免費進行所有交談。
基礎知識10:每個React組件都有一個故事(第2部分) (Fundamental 10: Every React component has a story (part 2))
Now that we know about the state of a component and how when that state changes some magic happens, let’s learn the last few concepts about that process.
既然我們已經知道了組件的狀態以及該狀態如何發生變化,那么就會發生一些不可思議的事情,讓我們學習有關該過程的最后幾個概念。
- A component might need to re-render when its state gets updated or when its parent decides to change the props that it passed to the component 當組件的狀態得到更新或其父對象決定更改傳遞給組件的道具時,可能需要重新渲染組件
If the latter happens, React invokes another lifecycle method,
componentWillReceiveProps
.如果發生后者,React將調用另一個生命周期方法
componentWillReceiveProps
。If either the state object or the passed-in props are changed, React has an important decision to do. Should the component be updated in the DOM? This is why it invokes another important lifecycle method here,
shouldComponentUpdate
. This method is an actual question, so if you need to customize or optimize the render process on your own, you have to answer that question by returning either true or false.如果更改了狀態對象或傳入的道具,React將做出重要決定。 是否應在DOM中更新組件? 這就是為什么它在這里調用另一個重要的生命周期方法
shouldComponentUpdate
。 這個方法是一個實際的問題,所以如果你需要定制或自己進行優化渲染過程中,可以通過返回true 或 false來回答這個問題。If there is no custom
shouldComponentUpdate
specified, React defaults to a very smart thing that’s actually good enough in most situations.如果沒有指定自定義的
shouldComponentUpdate
,React默認會是一個非常聰明的東西,實際上在大多數情況下已經足夠了。First, React invokes another lifecycle method at this point,
componentWillUpdate
. React will then compute the new rendered output and compare it with the last rendered output.首先,React此時會調用另一個生命周期方法
componentWillUpdate
。 然后,React將計算新的渲染輸出并將其與最后渲染的輸出進行比較。- If the rendered output is exactly the same, React does nothing (no need to talk to Mr. Browser). 如果渲染的輸出完全相同,則React不執行任何操作(無需與Browser先生交談)。
- If there is a difference, React takes that difference to the browser, as we’ve seen before. 如果存在差異,React會將差異引入瀏覽器,如我們之前所見。
In any case, since an update process happened anyway (even if the output was exactly the same), React invokes the final lifecycle method,
componentDidUpdate
.無論如何,由于無論如何都會發生更新過程(即使輸出完全相同),因此React會調用最終的生命周期方法
componentDidUpdate
。
Lifecycle methods are actually escape hatches. If you’re not doing anything special, you can create full applications without them. They’re very handy for analyzing what is going on in the application and for further optimizing the performance of React updates.
生命周期方法實際上是逃生艙口。 如果您沒有做任何特別的事情,則可以在沒有它們的情況下創建完整的應用程序。 對于分析應用程序中發生的事情以及進一步優化React更新的性能,它們非常方便。
That’s it. Believe it or not, with what you learned above (or parts of it, really), you can start creating some interesting React applications. If you’re hungry for more, check out my Learn React.js by Building Games book!
而已。 信不信由你,使用上面的知識(或部分知識),您可以開始創建一些有趣的React應用程序。 如果您渴望獲得更多,請查看我的《 Building Games學習Learn.js》一書!
Thanks to the many readers who reviewed and improved this article, ?ukasz Szewczak, Tim Broyles, Kyle Holden, Robert Axelse, Bruce Lane, Irvin Waldman, and Amie Wilt.
?ukaszSzewczak,Tim Broyles,Kyle Holden,Robert Axelse,Bruce Lane,Irvin Waldman和Amie Wilt感謝許多閱讀和改進本文的讀者。
Learning React or Node? Checkout my books:
學習React還是Node? 結帳我的書:
Learn React.js by Building Games
通過構建游戲學習React.js
Node.js Beyond the Basics
超越基礎的Node.js
翻譯自: https://www.freecodecamp.org/news/all-the-fundamental-react-js-concepts-jammed-into-this-single-medium-article-c83f9b53eac2/