React 学习笔记(上)

内容概要:

  • React介绍
  • Single Page Application & Multiple page Application
  • JSX 介绍
  • class-based component & functional component
  • props & state
  • styling
  • Handle List
  • Life cycle Hooks

1. What is React?

It is a JavaScript Library for building User Interfaces.

和Angular相似,React 也将整个Project 分为小的 components, 便于管理和重复使用。

React components

2. Single Page Application vs Multi Page Application

SPA MPA
Only get back one HTML file from the server.
We get this back at the first time as user visit the page.
We get back multiple HTML pages, where each page has the content for a given route
Content is rendered on Client Content is rendered on server
Every component is managed by React and the entire page is also managed by a root React component. Only some widgets (like a image gallery) is mamaged by React.



image.png

  • Create React App (简称CRA)

CRA是创建 React 应用的一个脚手架,它与其他脚手架不同的一个地方就是将一些复杂工具(比如 webpack)的配置封装了起来,让使用者不用关心这些工具的具体配置,从而降低了工具的使用难度。

npm install -g create-react-app

create-react-app my-app
cd my-app/
npm start


3. What is JSX?

It is a syntax extension to JavaScript.

const element = <h1>Hello world!</h1>

After compilation, JSX expressions become regular JavaScript function all and evalute to JavaScript objects.

This means that you can use JSX inside of if statement and for loops, assign it to variables, accept it as arguments, and return it from functions.

function getGreeting(user) {
  if (user) {
    return <h1>Hello, {formatName(user)}!</h1>;  }
  return <h1>Hello, Stranger.</h1>;}
  • JSX Prevents Injection Attacks
const title = response.potentiallyMaliciousInput;
// This is safe:
const element = <h1>{title}</h1>;

Because by default, React DOM escapes any values embedded in JSX before rendering them. Everything is converted to a String before rendered. This helps prevent XSS (cross-site-scripting) attacks.

  • React.createElement()
    Babel compiles JSX down to React.createElement() calls.

These two examples are identical:

const element = (
  <h1 className="greeting">
    Hello, world!
  </h1>
);
-------------------------------------------------------------------------------
const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world!'
);


4.Component

Components are the core building block of React apps. Actually, React is just a library for creating components in its core.

A typical React app therefore could be depicted as a component tree - having one root component ("App") and then an potentially infinite amount of nested child components.

Each component needs to return/ render some JSX code - it defines which HTML code React should render to the real DOM in the end.

JSX is NOT HTML but it looks a lot like it. Differences can be seen when looking closely though (for example className in JSX vs class in "normal HTML"). JSX is just syntactic sugar for JavaScript, allowing you to write HTMLish code instead of nested React.createElement(...) calls.

  • Two different ways to create components:

Functional components (also referred to as "presentational", "dumb" or "stateless" components ):

const cmp = () => { return <div>some JSX</div> }
(using ES6 arrow functions as shown here is recommended but optional)

class-based components (also referred to as "containers", "smart" or "stateful" components) :

class Cmp extends React.Component { render () { return <div>some JSX</div> } }
We'll of course dive into the difference throughout this course, you can already note that you should use 1) as often as possible though. It's the best-practice.

functional vs. class-based

不论用哪种方式创建component, component名称必须首字母大写。

  • 添加动态内容

1). 将简短的可执行JS代码放在大括号 {} 内
<p>I am a person and I am {Math.floor(Math.random()*30)} years old.</p>

2). 使用 props,实现写入动态数据:
Props is an object that includes all attributes you added to your JSX code. It allows you to pass data from a parent (wrapping) component to a child (embedded) component.

const person = (props) => {
  return <p>I am {props.name} and I am {Math.floor(Math.random()*30)} years old.</p>
}
class Person extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
  • Children props
    It is a revserved word, it refers to any elements between an open and closed tag of our components.

  • 使用PropTypes

npm install --save prop-types ----- 使用 npm 安装

可以在component中导入并引用,避免外部传入错误类型的props。当外部传入props类型与定义类型不一致时报错。

import React, {Component} from 'react'
import classes from'./Person.module.css';
import Aux from '../../../hoc/Auxiliary';
import withClass from '../../../hoc/WithClasses';
import PropTypes from 'prop-types';

class Person extends Component{
    render(){
        console.log('[Person.js] rendering...');
        const style = {
            color: 'red',
            textDecoration: 'underline',
            cursor: 'pointer'
        }

        return (
            <Aux>
                <p >I am {this.props.name} and I am {this.props.age} years old.</p>
                <p>{this.props.children}</p>
                <input type="text" onChange={this.props.change} value={this.props.name}/>
                <br></br>
                <p onClick={this.props.click} style={style}> delete me</p>
            </Aux>
        )
    }
};

Person.propTypes = {
    click: PropTypes.func,
    name: PropTypes.string,
    age: PropTypes.number,
    change: PropTypes.func
}


export default withClass(Person, classes.Person);



3). 使用State

使用props或者state 从父组件向子组件传递数据。

Pure: Functions that do not try to change their own inputs, and always return the same result for the same inputs.
Impure: Functions that will change their own inputs.

All Reacl components must act like pure functions with respect to their props. State allows React components to changes their output over time in response to user actions, network responses, and anything else, without violationg the rule.

State is used to change the component state from within. Only class-based components can define and use state.
state simply is a property of the component class, you have to call it state though - the name is not optional. You can then access it via this.state in your class JSX code (which you return in the required render() method).

Whenever state changes (taught over the next lectures), the component will re-render and reflect the new state. The difference to props is, that this happens within one and the same component - you don't receive new data (props ) from outside!

  • setState()
    Always use setState() function to change state values.
    如果直接用等号赋值修改,react 不会识别。 在React V16.8 之前,functional components中不能访问state,且不能使用setState()方法。
// Wrong
this.state.comment = 'Hello';

// Correct
this.setState({comment: 'Hello'});


  • When you want to update a state depending on its old value:
 state = {
    changeCounter: 0
  };

// Wrong
this.setState({
        changeCounter: this.state.changeCounter+1
      })

Beacuse setStatedoes not immediately trigger an update of the state of this component in a re-render cycle. Instead it is scheduled by React and React will then perform the state update and re-render cycle when it has the resources to do that.
In other words, the update could be instantly in simple applications, but it is not guaranteed. So, sometimes, the value you are depending on could be an unexpected value.

setState() 方法可以接受一个 Javascript 对象,也可以接受一个arrow function,在此匿名方法中,返回你的JavaScript 对象。

Here is the correct way:

this.setState((prevState, props) => {
        return{
          people:persons,
        changeCounter: prevState.changeCounter+1
        }
      }


  • useState Hook

    Use React Hook useState() to initialize and set states. React V16.8 之后,可以使用 useStatehook 在functional component 中操控state

    The useState hook always return an array with two elements:

    1. The current state object.
    2. A function to set current state values.
const [personState, setpersonState] = useState({
      people: [
        {name: 'Acde', age: 39},
        {name: 'Bob', age: 22},
        {name: 'Carial', age: 29},
      ]
  });


  • 使用props&state 跨组件通讯
    示例:
// In parent component
const switchNameHandler = (newName) => {
    setpersonState({
        people:[
          {name: newName, age: 39},
          {name: 'Bob', age: 44},
          {name: 'Carial', age: 99},
        ]}
  )};

// In return
//Two ways to pass the parament, the first will be more efficient
      <button onClick={switchNameHandler.bind(this, 'ChangedByButton')}>Switch Name</button> 
      <Person name={personState.people[0].name} age={personState.people[0].age} click={() => switchNameHandler('ChangedByPerson')}/>
// In child component
import React from 'react'

const person = (props) => {
return (
    <div>
        <p onClick={props.click}>I am {props.name} and I am {props.age} years old.</p>
        <p>{props.children}</p>
    </div>
    )
}

export default person;


5. Conditionally Rendering

1). Logical && operator

return (
    <div>
      <h1>Hello!</h1>
      {unreadMessages.length > 0 &&
        <h2>
          You have {unreadMessages.length} unread messages.
        </h2>
      }
    </div>
  );

2). 在{}中使用三元表达式(ternary expression) condition ? trueActions : faseActions

return(
      {
        showPerson === true ?
        <div>
        <Person name={personState.people[0].name} age={personState.people[0].age} click={() => switchNameHandler('ChangedByPerson')}/>
        <Person name={personState.people[1].name} age={personState.people[1].age} change={nameChangedHandler}> My hobbies: Racing</Person>
        <Person name={personState.people[2].name} age={personState.people[2].age}/>
      </div> : null
      }
)

3). 使用if-else condition 将需要动态展示的JSX代码打包

let persons = null;
  if(showPerson){
    persons = (
      <div>
        <Person name={personState.people[0].name} age={personState.people[0].age} click={() => switchNameHandler('ChangedByPerson')}/>
        <Person name={personState.people[1].name} age={personState.people[1].age} change={nameChangedHandler}> My hobbies: Racing</Person>
        <Person name={personState.people[2].name} age={personState.people[2].age}/>
      </div>
    )
  }

return(
   {persons}
)


  • List
    不同于Angular中的ngFor,React使用Javascript中自带的map() 方法,遍历list
let persons = null;
  if(showPerson){
    persons = (
      <div>
        {personState.people.map((person, index) => {
          return <Person 
            click ={deletePersonHandler.bind(this, index)}
            name={person.name} 
            age={person.age}
            key={person.id}/>
        })}
      </div>
    )
  }


  • map():
    注意,map方法只能遍历数组对象,如果需要遍历String,需要将String先转化成Array Object. 否则报错,str.map() is not a function..

String -> Array: [...myStr], myStr.split("")
Array -> String: myArr.join('')


  • key
    使用此种方法时,需要加上key property. The key here should be something unique, which allows React to compare the difference between list items. So that we don't need to rerender the whole list everytime.


6. Styling React Component & Element

  • Set Styles Dynamically:
    通过JavaScript的方式,在相应的condition中修改相关style属性。
const style = {
    backgroundColor: 'rgb(188, 245, 218)',
    font: 'inherit',
    border: '1x solid blue',
    padding: '8px',
    cursor: 'pointer',
    marginRight: '10px',
    ':hover': {
      backgroundColor: 'rgb(155, 209, 177)',
      color: 'white'
    }
  }

if(someCondition){
  ...
  style.backgroundColor = 'rgb(247, 171, 171)';
}


  • Dynamic add css classes:
    创建一个空数组,在不同条件下,push css styles
// In CSS file
.red{
  color: brown;
}

.bold{
  font-weight: bold;
}
//In JS file
const classes = [];
  if(condition1){
    classes.push('red');
  }
  if(condition2){
    classes.push('bold');
  }

return(
  <h1 className={classes.join(' ')}>Hello, {props.name}</h1>
)


  • Using Radium:
    Radium is a package that helps us with styling React Components.
    引入Radium可以让我们在自定义的inline style中使用 css psudo selector,以及media query.

步骤:

  1. npm install --save radium
  2. import Radium from 'radium'
  3. Wrap function name when export: export default Radium(App);
  4. 使用media query时:
    import {StyleRoot} from 'radium'
    <StyleRoot> All content in App.js return </StyleRoot>


  • Using hover:
import Radium, {StyleRoot} from 'radium';
const App = props => {
  const style = {
    backgroundColor: 'rgb(188, 245, 218)',
    font: 'inherit',
    border: '1x solid blue',
    padding: '8px',
    cursor: 'pointer',
    marginRight: '10px',
    ':hover': {
      backgroundColor: 'rgb(155, 209, 177)',
      color: 'white'
    }
  }
  return(
    <button
        style={style}
        onClick={togglePersonHandler}
        key = 'toggleBtn'
      >TogglePerson</button>
  )
}

export default Radium(App);


  • Using media query:
import Radium from 'radium';
const person = (props) => {
    const style = {
        '@media (min-width: 700px)' : {
             width: '450px'
        }
    };

return (
    <div className="Person" style={style}>
        ... Any content
    </div>
    )
}

export default Radium(person);

--------------------------------------------------In App.js---------------------------------------------------
return (
    <StyleRoot>
     ...Any content
      <Person/>
    </StyleRoot>
  )


  • CSS Modules:

    CSS Modules are a relatively new concept (you can dive super-deep into them here: https://github.com/css-modules/css-modules). With CSS modules, you can write normal CSS code and make sure, that it only applies to a given component.

    It's not using magic for that, instead it'll simply automatically generate unique CSS class names for you. And by importing a JS object and assigning classes from there, you use these dynamically generated, unique names. So the imported JS object simply exposes some properties which hold the generated CSS class names as values.

Example:

In Post.css File

 .Post  {
 color: red;
 }

In Post Component File

 import classes from  './Post.css';
   const post =  ()  =>  (
      <div className={classes.Post}>...</div>
 );

Here, classes.Post refers to an automatically generated Post property on the imported classes object. That property will in the end simply hold a value like Post__Post__ah5_1 .

So your .Post class was automatically transformed to a different class (Post__Post__ah5_1 ) which is unique across the application. You also can't use it accidentally in other components because you don't know the generated string! You can only access it through the classes object. And if you import the CSS file (in the same way) in another component, the classes object there will hold a Post property which yields a different (!) CSS class name. Hence it's scoped to a given component.

By the way, if you somehow also want to define a global (i.e. un-transformed) CSS class in such a .css file, you can prefix the selector with :global .

Example:

:global .Post { ... }

Now you can use className="Post" anywhere in your app and receive that styling.


7. Component Lifecycle

部分内容来源于:React life cycle in-depth

lifecycle hooks

Thay are Only available in Class-based Components
React will execute them for us automatically and they will run at different points of time and we can do different things with them. For example, fetch data from the web or do some cleanup work before a component is removed from the DOM.

React life cycle 遵循 “创建,更新,死亡” 的发展规律, 可分为3个阶段:

1). 出生/创建

这个阶段是开发人员初始化一个组件(Component)的阶段,包括定义和配置propsstate。这个component和其所有的子组件都会被装载到本地的UI堆栈上(Native UI Stack),例如DOM或UIVIew。
一个组件的出生/创建阶段只会发生一次。

constructor()
getDefaultProps() (React.createClass) or MyComponent.defaultProps (ES6 class)
getInitialState() (React.createClass) or this.state = ... (ES6 constructor)
getDerivedStateFromProps()
componentWillMount()
render()
Children initialization & life cycle kickoff
componentDidMount()

creation lifecycle


  • render()
    在render() 方法中,我们通过JSX创建元素,并将他们返回。不能在render()中修改任何state。

    The render() method being called does not immediately also render this to the real DOM. Instead, it compares the virtual DOM. It has an old virtual DOM and a Re-render virtual DOM. The virtual DOM is faster than the real DOM.

    React compares the old virtual DOM with the new one and checks if there is any differences. If there are some differences, it reaches out to the real DOM and only updates the places where differences are detected.

    How React render the DOM


  • componentDidMount()

    This method is called once all our children Elements and our Component instances are mounted onto the native UI. When this method is called we now have access to the Native UI (DOM, UIView, etc.), access to our children refs and the ability to potentially trigger a new render pass.

执行render 方法时,遍历顺序为从上到下。

A -> A.0 -> A.0.0 -> A.0.1 -> A.1 -> A.2.

componentDidMount 则相反,其顺序为从下至上。

A.2 -> A.1 -> A.0.1 -> A.0.0 -> A.0 -> A

通过倒序遍历,可以确保每个子组件都以安置,且执行了自己的componentDidMount() 方法。这使得父组件可以访问其自己的UI element和其所有子组件。

Useful Task:
1).componentDidMount() 最通常的用途为处理UI交互和初始化第三方 UI 库。

For example, we may need to make changes to our current state based on how the Native UI laid out our content. We may need to figure out the current width/height of our children or our own instance. This is especially helpful in the browser where CSS layout drives a lot of our DOM calculations.

Another useful task is setting up 3rd party UIs. For example, if we wanted to use a library like C3.js or the Date Range Picker, this is where we would initialize our UI libraries.

2). Making HTTP requests.



2). 成长/更新

在此阶段中,开发人员会更新props,state,处理用户交互,进行跨组件通讯。这个阶段也是我们在一个组件的生命周期中花费时间最多的阶段。
与一三两阶段不同的是,我们往往会不断重复 “生长/更新” 阶段。

getDerivedStateFromProps()
componentWillReceiveProps()
shouldComponentUpdate()
componentWillUpdate()
render()
Children Life cycle methods
getSnapshotBeforeUpdate()
componentDidUpdate()

update lifecycle


  • shouldComponentUpdate(): A hook to optmize performance
    This method allows your Component to exit the Update life cycle if there is no reason to apply a new render. Out of the box, the shouldComponentUpdate() is a no-op that returns true. This means every time we start an Update in a Component, we will re-render.
    The Goal is to Prevent unnecessary renders.

    在此方法中,我们可以判断现有的props & statenext prop & next state是否继续一致。如果一致则返回false,如果不同则返回true.

 shouldComponentUpdate(nextProps, nextState) {
    return nextProps.show !== this.props.show
}

如果需要比较组件中所有props是否发生了任意变化,则可以不是用此方法一一比较,采用extends PureComponent

  • PureComponent
    It is a normal component that already implements shouldComponentUpdate with a complete props check. It checks any changes in any props of that component.
    You can use it by class App extends PureComponent.

  • React.memo()
    When React.memo() wraps a component, React memoizes the rendered output then skips unnecessary rendering.

  • useEffect
    useEffect is a React Hook, which allows you to combines the functionality or the use cases you cover of all these class-base lifecycle hooks in one React hook.

    -- It takes a default function for every render cycle. It runs for every update.
    -- You can also return a function, which runs BEFORE the main useEffect function runs, but AFTER the first render cycle.
    -- To control the behavior of useEffect, there is a second parameter. It is an array, where you simply point at all the data that actually are used in your effect.

    For the example code below:
    It only executes when people change props.
    If the array is empty, it will only run for the first time when the app gets rendered.
    If you don't pass an array as a second parameter, it will run on every ender cycle.

 useEffect(() => {
        console.log('[Cockpit.js] useEffect');
        
        setTimeout(()=>{
            alert("This is from useEffect");
        }, 1000);
    }, [props.people]);



3). 死亡/卸载
此阶段往往发生于当一个组件从DOM上被移除(unmounted)的时候。通常是用户导航离开,UI页面改变,组件暂时被隐藏等情况发生时。
此阶段只发生一次,并为组件被垃圾回收做出准备。

This method is used to handle memory leaks.

componentWillUnmount()
Children Life cycle methods
Instance destroyed for Garbage Collection

PS: 被划去的方法已被React取消或由其他方法取代,将于React17 停用。

8. Virtual DOM

A virtual DOM object is a representation of a DOM object, like a lightweight copy.
React updates the view by comparing different versions of virtual DOMs.
































最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,607评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,239评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,960评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,750评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,764评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,604评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,347评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,253评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,702评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,893评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,015评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,734评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,352评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,934评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,052评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,216评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,969评论 2 355