个人笔记, 转载请注明
转载自 szhshp的第三边境研究所
Refs and the DOM
In the typical React dataflow, props are the only way that parent components interact with their children. To modify a child, you re-render it with new props. However, there are a few cases where you need to imperatively modify a child outside of the typical dataflow. The child to be modified could be an instance of a React component, or it could be a DOM element. For both of these cases, React provides an escape hatch.
一般情况下想要刷新child elem必须通过新的prop刷新, 但是其实可以有另一个方法, 就是使用
ref
When to Use Refs
There are a few good use cases for refs:
- Managing focus, text selection, or media playback.
- Triggering imperative animations.
- Integrating with third-party DOM libraries.
Avoid using refs for anything that can be done declaratively.
For example, instead of exposing open()
and close()
methods on a Dialog
component, pass an isOpen
prop to it.
使用
ref
之前切记要确定一下对应的属性是不是可以使用prop代替, 开发时应该尽可能遵从React推荐的信息流
Adding a Ref to a DOM Element
React supports a special attribute that you can attach to any component. The ref
attribute takes a callback function, and the callback will be executed immediately after the component is mounted or unmounted.
关于ref
有这些特点:
- 可以给任何DOM提供这个attr
- 提供给ref的value应该是个callback
- 这个callback可以在
mounted/unmounted
的时候调用
- 注意会在
mounted/unmounted
的时候执行一次, 而不是每次渲染的时候执行
- 当提供这个参数给HTML elem的时候, 这个
ref
指向的callback会获取当前DOM elem作为参数.
例子:
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.focusTextInput = this.focusTextInput.bind(this);
}
focusTextInput() {
// Explicitly focus the text input using the raw DOM API
// 3. 因此这儿可以通过this.textInput调用focus()方法
this.textInput.focus();
}
render() {
// Use the `ref` callback to store a reference to the text input DOM
// element in an instance field (for example, this.textInput).
// 1. Component载入的时候就会执行ref里面的callback.
// 2. 然后这个Elem会当做参数传入, 因此就可以通过this.textInput引用这个DOM elem
return (
<div>
<input
type="text"
ref={(input) => { this.textInput = input; }} />
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
Adding a Ref to a Class Component
类似
ref
在HTML DOM中的使用, 将ref
赋值给自定义Component的时候,ref
对应的callback就会接受自身这个instance作为参数注意: 必须要这个自定义Component是个完整的class, 不可以是函数
When the ref
attribute is used on a custom component declared as a class, the ref
callback receives the mounted instance of the component as its argument. For example, if we wanted to wrap the CustomTextInput
above to simulate it being clicked immediately after mounting:
class AutoFocusTextInput extends React.Component {
componentDidMount() {
// 2. 这儿就可以对这个instance进行引用
this.textInput.focusTextInput();
}
render() {
return (
<CustomTextInput
// 1. 对于自定义的Component, ref对应的callback会将自身这个instance当做参数传进去
ref={(input) => { this.textInput = input; }} />
);
}
}
Exposing DOM Refs to Parent Components
一般数据流不应该在Parent Class里面控制Child Class, 但是偶尔就有这样傻逼的需求.
这时候用ref
最适合了, 但是依然不推荐这么使用.
更好的解决办法是你和需求分析的人打一架然后让他根据代码改需求
当然上文提到了解决办法是让child class暴露一个prop, 但是这个办法无法对function Component
使用.
另外下面这个办法对function Component
也有用:
function CustomTextInput(props) {
// 2. 在child component里面将这个callback赋值给ref
// 2+. 当然这儿只能用ref这个attr
// 3. 然后这个child component在mount/unmount的时候就会执行这个callback
return (
<div>
<input ref={props.inputRef} />
</div>
);
}
class Parent extends React.Component {
render() {
// `Parent` passes its ref callback as an `inputRef`prop to the `CustomTextInput`
// 1. 这儿提供一个callback作为props.inputReg
return (
<CustomTextInput
inputRef={el => this.inputElement = el}
/>
);
}
}
最终在parent component
中就可以通过inputElement
这个变量调用到对应输入框元素.
而且这个可以逐级传递, 比如下面这个隔代传递的例子:
function CustomTextInput(props) {
return (
<div>
<input ref={props.inputRef} />
</div>
);
}
function Parent(props) {
return (
<div>
My input: <CustomTextInput inputRef={props.inputRef} />
</div>
);
}
class Grandparent extends React.Component {
render() {
return (
<Parent
inputRef={el => this.inputElement = el}
/>
);
}
}
看起来暴露DOM有些别扭, 但是很多时候需求就是比较特别. 因此上面是推荐用法.
旧版本React里面曾经有过使用
this.refs.textInput
的用法, 其中ref会指向一段字符串, 新版本React不推荐如此使用
注意事项
如果ref
是个inline function, updates的时候会被调用两次. 第一次是null
然后第二次是DOM Elem.
这个是因为update的时候React需要清理旧的并且建立新的component
Solutions
为了防止这个问题, 可以将ref callback和对应class进行绑定.
即使不绑定也不会引发太多问题
JSX In Depth
JSX将通过Babel进行编译.
标准首位标签格式
<MyButton color="blue" shadowSize={2}>
Click Me
</MyButton>
compiles into:
React.createElement(
MyButton,
{color: 'blue', shadowSize: 2},
'Click Me'
)
自闭合标签格式
You can also use the self-closing form of the tag if there are no children. So:
<div className="sidebar" />
compiles into:
React.createElement(
'div',
{className: 'sidebar'},
null
)
Specifying The React Element Type
一般Component使用大写字母开头, 如果在当前JSX里面使用<XXX/>
那么XXX这个Component就必须出现在Scope之内.
另外可以使用
import
语句将Component引用到Scope内
Using Dot Notation for JSX Type
import React from 'react';
const MyComponents = {
DatePicker: function DatePicker(props) {
return <div>Imagine a {props.color} datepicker here.</div>;
}
}
//下方使用圆点标识符调用了子属性
function BlueDatePicker() {
return <MyComponents.DatePicker color="blue" />;
}
User-Defined Components Must Be Capitalized
自定义Component(比如 <MyComponent/>
)应该大写字母开头
系统自带的Component(比如 <div/>
)应该小写字母开头
否则系统会抛出警告
Choosing the Type at Runtime
注意JSX Obj的tags必须是完整的单词, 如果想要动态生成tag名称可以用赋值的办法
错误的例子:
function Story(props) {
// Wrong! JSX type can't be an expression.
return <components[props.storyType] story={props.story} />;
}
正确的做法:
function Story(props) {
// Correct! JSX type can be a capitalized variable.
const SpecificStory = components[props.storyType];
return <SpecificStory story={props.story} />;
}
Prop in JSX
JavaScript Expressions as Props
关于使用JS表达式作为prop:
<MyComponent foo={1 + 2 + 3 + 4} />
If condition within JSX
不能够直接使用, 但是可以用这个替代方法:
function NumberDescriber(props) {
let description;
// check the condition
if (props.number % 2 == 0) {
description = <strong>even</strong>;
} else {
description = <i>odd</i>;
}
return <div>{props.number} is an {description} number</div>;
}
另外一个很实用的条件判断的snippet
This can be useful to conditionally render React elements. This JSX only renders a <Header />
if showHeader
is true
:
<div>
{showHeader && <Header />}
<Content />
</div>
### String Literals
When you pass a string literal, its value is HTML-unescaped. So these two JSX expressions are equivalent:
<MyComponent message="<3" />
<MyComponent message={'<3'} />
### Props Default to “True”
If you pass no value for a prop, it defaults to true. These two JSX expressions are equivalent:
<MyTextBox autocomplete />
<MyTextBox autocomplete={true} />
### Spread Attributes
If you already have `props` as an object, and you want to pass it in JSX, you can use `...` as a “spread” operator to pass the whole props object. These two components are equivalent:
>提供Props时, 若有一个Obj并且这个Obj里面所有Attr和需要的Prop相匹配, 那么可以使用`spread` operator 进行赋值
function App1() {
return <Greeting firstName="Ben" lastName="Hector" />;
}
function App2() {
const props = {firstName: 'Ben', lastName: 'Hector'};
return <Greeting {...props} />;
}
另外对于`spread` operator也可以给部分值赋值:
const Button = props => {
//将传进函数里面的props.kind 赋值给kind, 将其他属性赋值给...other
const { kind, ...other } = props;
const className = kind === "primary" ? "PrimaryButton" : "SecondaryButton";
//然后将...other传给button这个elem, 而没有传递kind这个参数
return <button className={className} {...other} />;
};
const App = () => {
return (
<div>
<Button kind="primary" onClick={() => console.log("clicked!")}>
Hello World!
</Button>
</div>
);
};
> # 关于解构运算符
>
>MDN参考:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator
>
这玩意是ES6新引进的运算符, 通过三个点进行引用`...`
>
> ```
>function sum(x,y,z){
return x+y+z
}
>
console.log(sum(...[1,2,3])) //6
console.log(sum(...[1,2,3,4,5])) //6
console.log(sum(...[1,,3,4,5])) //NaN
>
var doSomething = function(...args){
return sum(..args)
}
>
console.log(doSomething(...[1,2,3])) //6
>
//对Object是否有效呢?
var obj = { a:"A", b:"B", c:"C", d:"D", e:"E" }
var {a,b,...others} = obj //报错var {a,b,...others} = obj //报错
在老版本ES6中, 对Obj进行解构赋值会报错, 但是JSX中允许这样的使用.
Children in JSX
子元素全部会通过props.children
进行引用.
另外如果没有子tag而只是放置了一段文本, 那么props.children
就会是tag之间的一段字符串
Hint: HTML is unescaped!
<MyComponent>Hello world!</MyComponent>
<div>This is valid HTML & JSX at the same time.</div>
tags之间的空格和空行都会被忽略.
<div>
Hello World
</div>
另外也可以将JS表达式用于child elem. 比如一个很典型的使用数组循环生成列表的例子:
return (
<ul>
{todos.map((message) => <Item key={message} message={message} />)}
</ul>
);
Functions as Children
反正只要最后返回的是一个完整的JSX Obj即可
// 2. 我们在渲染parent elem
function Repeat(props) {
let items = [];
for (let i = 0; i < props.numTimes; i++) {
// 3. 通过调用child elem对应的函数生成jsx obj
items.push(props.children(i));
}
return <div>{items}</div>;
}
// 1. 返回的JSX obj的child elem是个函数
function ListOfTenThings() {
return (
<Repeat numTimes={10}>
{(index) => <div key={index}>This is item {index} in the list</div>}
</Repeat>
);
}
Booleans, Null, and Undefined Are Ignored
false
, null
, undefined
, and true
are valid children. They simply don’t render. These JSX expressions will all render to the same thing:
<div />
<div></div>
<div>{false}</div>
<div>{null}</div>
<div>{undefined}</div>
<div>{true}</div>
Thinking in React
就是React项目流程的rethinking
Step 1: Break The UI Into A Component Hierarchy
- draw boxes around every component (and subcomponent) in the mock and give them all names
- 使用一些规则(比如单一原则)判断元素应该属于哪些部分
[图片上传失败...(image-af5c7e-1512723756201)]
- 得到Component层次列表
- 决定数据模型
Step 2: Build A Static Version in React
- 先写个静态的
- 重用的Component要准备好
- 静态的部分应该不需要用到任何State, State应该仅仅用于动态的Component
- 从上往下或者从下往上建立都可以, 小型项目从上往下会快一些, 大型项目反之
- 改为动态模型
- 一般最大的那个Component会接受数据作为全部数据源
Step 3: Identify The Minimal (but complete) Representation Of UI State
- 建立项目的时候, 最好能先确定minimal set of mutable state, 也就是最小动态模块
- 判断一下动态模块是否可以设置成state
之前提到过的三个原则进行判断:
- Is it passed in from a parent via props? If so, it probably isn’t state.
- Does it remain unchanged over time? If so, it probably isn’t state.
- Can you compute it based on any other state or props in your component? If so, it isn’t state.
Step 4: Identify Where Your State Should Live
- 决定哪些Component应该拥有这些State, 哪些Component应该让State进行变化
React从头到尾就只有一条数据流有这些需要注意的:
- 可以通过自身State进行render的Component
- 最大的那个Component应该拥有State, 用于流程管理之类的业务, 这个State可能包含所有动态成分的内容
- 如果无法找到一个值得拥有一个State的Component, 那就写个新的, 可能可以放在最大的State的更上一层
Step 5: Add Inverse Data Flow
就是数据的各种交互呗
一个重要的概念:
有些时候parent class
可能会给child class
提供一个eventHandler()
这玩意可以当做一种callback
, 赋值给child class
的onchange, child class
通过调用这个onChange使得parent class
的state变化.
如果只有一个值的情况下甚至可以不用给
child class
设置任何的state# React: 关于setState()
React&Error: setState()不立刻响应到DOM, 总是慢一步
这个八成是因为在setState的同一个方法里面调用了this.state
这时候获取到的state都是前一个state
下方是参考文献:
原文: https://medium.com/@mweststra...
作者: Michel Weststrate
前言
这篇文章原标题是3 Reasons why I stopped using React.setState,但是我对原文作者提出的论点不是很感冒,但是作者提出的三点对React
新手来说是很容易忽略的地方,所以我在这里只提出部分内容,而且把标题改为使用React.setState需要注意的三点。
正文
对React
新手来说,使用setState
是一件很复杂的事情。即使是熟练的React
开发,也很有可能因为React
的一些机制而产生一些bug,比如下面这个例子:
文档中也说明了当使用setState
的时候,需要注意什么问题:
注意:
绝对不要 直接改变this.state
,因为之后调用setState()
可能会替换掉你做的改变。把this.state
当做是不可变的。
setState()
不会立刻改变this.state
,而是创建一个即将处理的state
转变。在调用该方法之后访问this.state
可能会返回现有的值。对
setState
的调用没有任何同步性的保证,并且调用可能会为了性能收益批量执行。
setState()
将总是触发一次重绘,除非在shouldComponentUpdate()
中实现了条件渲染逻辑。如果可变对象被使用了,但又不能在shouldComponentUpdate()
中实现这种逻辑,仅在新state
和之前的state
存在差异的时候调用setState()
可以避免不必要的重新渲染。
总结出来,当使用setState
的时候,有三个问题需要注意:
1. setState是异步的(译者注:不保证同步的)
很多开发刚开始没有注意到setState
是异步的。如果你修改一些state
,然后直接查看它,你会看到之前的state
。这是setState
中最容易出错的地方。 setState
这个词看起来并不像是异步的,所以如果你不假思索的用它,可能会造成bugs
。下面这个例子很好的展示了这个问题:
class Select extends React.Component {
constructor(props, context) {
super(props, context)
this.state = {
selection: props.values[0]
};
}
render() {
return (
<ul onKeyDown={this.onKeyDown} tabIndex={0}>
{this.props.values.map(value =>
<li
className={value === this.state.selection ? 'selected' : ''}
key={value}
onClick={() => this.onSelect(value)}
>
{value}
</li>
)}
</ul>
)
}
onSelect(value) {
this.setState({
selection: value
})
this.fireOnSelect()
}
onKeyDown = (e) => {
const {values} = this.props
const idx = values.indexOf(this.state.selection)
if (e.keyCode === 38 && idx > 0) { /* up */
this.setState({
selection: values[idx - 1]
})
} else if (e.keyCode === 40 && idx < values.length -1) { /* down */
this.setState({
selection: values[idx + 1]
})
}
this.fireOnSelect()
}
fireOnSelect() {
if (typeof this.props.onSelect === "function")
this.props.onSelect(this.state.selection) /* not what you expected..*/
}
}
ReactDOM.render(
<Select
values={["State.", "Should.", "Be.", "Synchronous."]}
onSelect={value => console.log(value)}
/>,
document.getElementById("app")
)
第一眼看上去,这个代码似乎没有什么问题。两个事件处理中调用onSelect
方法。但是,这个Select
组件中有一个bug
很好的展现了之前的GIF
图。onSelect
方法永远传递的是之前的state.selection
值,因为当fireOnSelect
调用的时候,setState
还没有完成它的工作。我认为React
至少要把setState
改名为scheduleState
或者把回掉函数设为必须参数。
这个bug很容易修改,最难的地方在于你要知道有这个问题。
2. setState会造成不必要的渲染
setState
造成的第二个问题是:每次调用都会造成重新渲染。很多时候,这些重新渲染是不必要的。你可以用React performance tools
中的printWasted来查看什么时候会发生不必要渲染。但是,大概的说,不必要的渲染有以下几个原因:
新的
state
其实和之前的是一样的。这个问题通常可以通过shouldComponentUpdate
来解决。也可以用pure render
或者其他的库来解决这个问题。通常发生改变的
state
是和渲染有关的,但是也有例外。比如,有些数据是根据某些状态来显示的。第三,有些
state
和渲染一点关系都没有。有一些state
可能是和事件、timer ID
有关的。
3.setState并不能很有效的管理所有的组件状态
基于上面的最后一条,并不是所有的组件状态都应该用setState
来进行保存和更新的。复杂的组件可能会有各种各样的状态需要管理。用setState
来管理这些状态不但会造成很多不需要的重新渲染,也会造成相关的生命周期钩子一直被调用,从而造成很多奇怪的问题。
后话
在原文中作者推荐了一个叫做MobX
的库来管理部分状态,我不是很感冒,所以我就不介绍。如果感兴趣的,可以通过最上面的链接看看原文中的介绍。
基于上面提出的三点,我认为新手应该注意的地方是:
setState
是不保证同步的
setState
是不保证同步的,是不保证同步的,是不保证同步的。重要的事情说三遍。之所以不说它是异步的,是因为setState
在某些情况下也是同步更新的。可以参考这篇文章
如果需要在setState
后直接获取修改后的值,那么有几个方案:
传入对应的参数,不通过this.state
获取
针对于之前的例子,完全可以在调用fireOnSelect
的时候,传入需要的值。而不是在方法中在通过this.state
来获取
使用回调函数
setState
方法接收一个function
作为回调函数。这个回掉函数会在setState
完成以后直接调用,这样就可以获取最新的state
。对于之前的例子,就可以这样:
this.setState({
selection: value
}, this.fireOnSelect)
使用setTimeout
在setState
使用setTimeout
来让setState
先完成以后再执行里面内容。这样子:
this.setState({
selection: value
});
setTimeout(this.fireOnSelect, 0);
直接输出,回调函数,setTimeout
对比
componentDidMount(){
this.setState({val: this.state.val + 1}, ()=>{
console.log("In callback " + this.state.val);
});
console.log("Direct call " + this.state.val);
setTimeout(()=>{
console.log("begin of setTimeout" + this.state.val);
this.setState({val: this.state.val + 1}, ()=>{
console.log("setTimeout setState callback " + this.state.val);
});
setTimeout(()=>{
console.log("setTimeout of settimeout " + this.state.val);
}, 0);
console.log("end of setTimeout " + this.state.val);
}, 0);
}
如果val默认为0, 输入的结果是:
> Direct call 0
> In callback 1
> begin of setTimeout 1
> setTimeout setState callback 2
> end of setTimeout 2
> setTimeout of settimeout 2
和渲染无关的状态尽量不要放在state
中来管理
通常state
中只来管理和渲染有关的状态,从而保证setState
改变的状态都是和渲染有关的状态。这样子就可以避免不必要的重复渲染。其他和渲染无关的状态,可以直接以属性的形式保存在组件中,在需要的时候调用和改变,不会造成渲染。或者参考原文中的MobX
。
避免不必要的修改,当state
的值没有发生改变的时候,尽量不要使用setState
。虽然shouldComponentUpdate
和PureComponent
可以避免不必要的重复渲染,但是还是增加了一层shallowEqual
的调用,造成多余的浪费。
以上
React: Form里面判断checkbox.md
handleStockedChange(e){
var value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
}
React: 关于是否应该将某个变量作为state.md
- Is it passed in from a parent via props? If so, it probably isn’t state.
- Does it remain unchanged over time? If so, it probably isn’t state.
- Can you compute it based on any other state or props in your component? If so, it isn’t state.# React&Error: Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null
八成就是return后面换行了, JS会自动加分号所以返回了undefined
React&Error: Unterminated JSX contents
这个八成就是写了两个elem或者某个elem没有闭合
Composition vs Inheritance
不推荐使用继承
我也不推荐
示例
Anything inside the <FancyBorder>
JSX tag gets passed into the FancyBorder component as a children prop.
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}
React: Form/Inputs
一般h5的input自己会管理自己的输入
Controlled Components
<span style="color: red;"> 实际上就是两点:</span>
- 给value绑定一个state的值,
state.value
最好 - 给onChange绑定一个事件, 修改
state.value
React也可以在渲染的时候绑定事件, 事件里面就可以进行各种操作, 比如转换所有文本为uppercase之类的。另外修改了文本之后也可以立刻反应/渲染到元素之上。
类似于将JS端的onChange放到了Server
handleChange(event) {
//通过变量event.target.value获取输入的值并且设置state
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}> //绑定onSubmit
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} /> //绑定onChange
</label>
<input type="submit" value="Submit" />
</form>
);
}
The textarea
Tag
有一点和H5不同的地方, React端的value的设置可以通过一个attr:
H5:
<textarea>
Hello there, this is some text in a text area
</textarea>
React:
<textarea value={this.state.value} onChange={this.handleChange} />
The select
Tag
设置selected的option的时候也略有不同, 同样可以通过value这个attr设置
H5:
<select>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option selected value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>
React:
<select value={this.state.value} onChange={this.handleChange}>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>
关于使用Value=Null来允许用户输入
可以看到, 将input的value设置为this.state.value
就是为了阻止用户自由输入, 但是有些时候要将controlled Input的权限放开, 因此我们可以将value设置为null
或者undefined
来开启用户自由输入.
ReactDOM.render(<input value="hi" />, mountNode);
// allow user input after 1sec
setTimeout(function() {
ReactDOM.render(<input value={null} />, mountNode);
}, 1000);
关于Controlled Components 和 UnControlled Components
缩写成CC/UC
CC写起来其实挺麻烦, 因为每个elem都要写个onChange.
其实也可以使用UC
使用UC的例子:
- 使用一个
ref
将输入的值赋给多个参数, 比如this.xxx
- submit的时候对
this.xxx
进行各种validation以及处理
handleSubmit(event) {
alert('A name was submitted: ' + this.input.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" ref={(input) => this.input = input} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
React: 关于使用Keys来索引列表.md
一般将key放置到列表class里面进行赋值, 而不是放到item里面进行赋值
最好放在ul循环生成li的时候给li添加key
function ListItem(props) {
const value = props.value;
return (
// Wrong! There is no need to specify the key here
// 当然在这儿也获得不到key值
<li key={value.toString()}>
{value}
</li>
);
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// Wrong! The key should have been specified here:
<ListItem value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
正确用法:
function ListItem(props) {
// Correct! There is no need to specify the key here:
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// Correct! Key should be specified inside the array.
<ListItem key={number.toString()}
value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
React: 关于render()里面隐藏一个元素.md
不要return一个JSX Obj, return null
即可
React: 关于使用Inline Expressions.md
If-then
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 && //注意这里, 前面其实是一个条件
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
);
If-Else
return (
<div>
The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
</div>
);
return (
<div>
{isLoggedIn ? (
<LogoutButton onClick={this.handleLogoutClick} />
) : (
<LoginButton onClick={this.handleLoginClick} />
)}
</div>
);
React: Handling Events
几个要点:
- React events are named using camelCase, rather than lowercase.
- With JSX you pass a function as the event handler, rather than a string.
For example, the HTML:
<button onclick="activateLasers()">
Activate Lasers
</button>
is slightly different in React:
<button onClick={activateLasers}>
Activate Lasers
</button>
Another difference is that you cannot return false
to prevent default behavior in React. You must call preventDefault
explicitly.
防止默认事件的代码需要一些修正
<a href="#" onclick="console.log('The link was clicked.'); return false">
Click me
</a>
In React, this could instead be:
function ActionLink() {
function handleClick(e) {
e.preventDefault(); // must be called implicitly
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
When using React you should generally not need to call addEventListener
to add listeners to a DOM element after it is created.
Instead, just provide a listener when the element is initially rendered.
初始化的时候给事件管理器,DOM初始化之后就别去添加其他监听器了。
JQuery: ...
示例
class Welcome extends React.Component{
constructor(props) {
super(props);
this.state = {isToggleOn: true};
this.handleClick = this.handleClick.bind(this); //绑定一下事件到this
}
handleClick() {
console.log(123)
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render(){
return (
<div onClick={this.handleClick}> //这里记得要写一个attr,并且要注意大小写
{(this.state.isToggleOn)?'A':'B'}, {this.props.name}
</div>
);
}
}
Passing Arguments to Event Handlers
下方的ID代表对应数据编号, 既可以使用外部的循环Index.
这儿有点像是闭包
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
两种模式类似, 重点在于一定要将React event传进去
- 对于Arrow Function来说, 需要显示传入
- 对于bind()来说就不用了
来源: https://reactjs.org/docs/handling-events.html
React&Error: Cannot call a class as a function.md
class component看起来不能通过函数的方式进行调用
那么就改成tag模式调用就行:
render(){
return (
<div>
<WelcomeLList nameList={nameList}/>
// {WelcomeList(this.props.nameList)}
// ↑↑ dont use this way if you created class component
</div>
);
}
React&Tips: 关于在Render里面使用变量.md
其实和Java的class一样,render里面可以直接调用prop参数了
class Root extends React.Component{
constructor(props) {
super(props);
}
render(){
return (
<div>
{WelcomeList(this.props.nameList)} //这里可以直接使用this.prop
</div>
);
}
}
在React中使用数组作为参数
简单易懂,直接赋值一个数组即可,不用加双引号,不然会被当做长字符串
var nameList = ["Mike","Jack","Dick"];
ReactDOM.render(
<Root nameList={nameList}/>,
// <Root nameList="{nameList}"/>, // This is wrong
document.getElementById('root')
);
React: 关于States.md
类似于Android的生命周期调节参数,此外state必须在定义它的那个class里面使用。
另外可以将state传到子元素,不过不能传给其他同级元素或者父元素
因此只可能出现Down Flow不可能向上传递。
另外stateful的Component和stateless的Component完全可以随意交叉使用,反正数据都可以相互传递
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
<FormattedDate date={this.state.date} />
生命周期
对于一个Component:
- 初始化的时候的操作叫做Mounting
- 销毁的操作叫做unMounting
然后可以在这两个事件进行监听
- componentDidMount()事件会在Component渲染成功时执行
- componentWillUnmount()事件会在Component销毁的时候执行
因此对于一个定时器来说,应该在componentDidMount()事件里面注册Timer并且在componentWillUnmount()事件里面关闭Timer
然后在Timer里面必须更新state数据,因为我们会将state数据输出出来,更新state需要执行setState(),将参数传进去就行
简单易懂
Timer的示例
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
Using State Correctly
几个关于setState()
的要点:
1. Do Not Modify State Directly
// Wrong
this.state.comment = 'Hello';
// Correct
this.setState({comment: 'Hello'});
The only place where you can assign this.state
is the constructor.
2. State Updates May Be Asynchronous
因此不能直接使用state来overwite state
React may batch multiple setState()
calls into a single update for performance.
Because this.props
and this.state
may be updated asynchronously, you should not rely on their values for calculating the next state.
For example, this code may fail to update the counter:
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
To fix it, use a second form of setState()
that accepts a function rather than an object. That function will receive the previous state as the first argument, and the props at the time the update is applied as the second argument:
写一个闭包可以解决问题
// Correct
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
3. State Updates are Merged
When you call setState()
, React merges the object you provide into the current state.
使用
setState()
的时候,只要没有提供value的state都不会改变
For example, your state may contain several independent variables:
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
Then you can update them independently with separate setState()
calls:
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
The merging is shallow, so this.setState({comments})
leaves this.state.posts
intact, but completely replaces this.state.comments
.
Converting a Function to a Class
- You can convert a functional component like Clock to a class in five steps:
- Create an ES6 class with the same name that extends React.Component.
- Add a single empty method to it called render().
- Move the body of the function into the render() method.
- Replace props with this.props in the render() body.
- Delete the remaining empty function declaration.
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
Adding Local State to a Class
We will move the date
from props to state in three steps:
- Replace
this.props.date
withthis.state.date
in therender()
method:
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
- Add a class constructor that assigns the initial
this.state
:
class Clock extends React.Component {
constructor(props) { //!!!!!!!!!!!!!!
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
Note how we pass props
to the base constructor:
constructor(props) {
super(props);
this.state = {date: new Date()};
}
Class components should always call the base constructor with props
.
- Remove the
date
prop from the<Clock />
element:
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
We will later add the timer code back to the component itself.
The result looks like this:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
来源: https://reactjs.org/docs/state-and-lifecycle.html
React: 关于Local States.md
React: JSX里面循环生成多个子元素.md
用这个方法可以循环生成多个子元素
function Welcome(props) {
props.name = 'test';
return <h1>Hello, {props.name}</h1>;
}
function WelcomeList(nameList) {
var nameDOM = [];
for (var i = 0; i < nameList.length; i++) {
nameDOM.push(Welcome({name: nameList[i]}))
}
return nameDOM;
}
function Root() {
var nameList = ["Mike","Jack","Dick"];
return (
<div>
{WelcomeList(nameList)}
</div>
);
}
ReactDOM.render(
<Root />,
document.getElementById('root')
);