React is a declarative, efficient, and flexible JavaScript library for building user interfaces.
It lets you compose complex UIs from small and isolated pieces of code called “components”.
注意
concat() 方法可能与你比较熟悉的 push() 方法不太一样,它并不会改变原数组,所以我们推荐使用 concat()
history : history.concat([
{
squares : squares,
}
]),
用slice 拷贝, 用setState来覆盖
const _squares = this.state.squares.slice();
_squares[i] = this.state.isXNext? “X” : “O”;
this.setState({
squares : _squares,
isXNext : !this.state.isXNext
});
js 的匿名函数
onClick = {(i) => this.handleClick(i)}
在 JavaScript 中,数组拥有 map() 方法,该方法通常用于把某数组映射为另一个数组,例如:
const numbers = [1, 2, 3];
const doubled = numbers.map(x => x * 2); // [2, 4, 6]
和scala 很像呀
key 是 React 中一个特殊的保留属性(还有一个是 ref,拥有更高级的特性)。当 React 元素被创建出来的时候,
React 会提取出 key 属性,然后把 key 直接存储在返回的元素上。
虽然 key 看起来好像是 props 中的一个,但是你不能通过 this.props.key 来获取 key。React 会通过 key 来自动判断哪些组件需要更新。组件是不能访问到它的 key 的。
我们强烈推荐,每次只要你构建动态列表的时候,都要指定一个合适的 key。
a. 在 JSX 语法中,你可以在大括号内放置任何有效的 JavaScript 表达式。例如,2 + 2,user.firstName 或 formatName(user) 都是有效的 JavaScript 表达式。
==
const name = ‘Josh Perez’;
const element =
Hello, {name}
;b. JSX 也是一个表达式
可以在if 和 for 以及return 中使用
c. 使用JSX对某一个属性进行设置
1.字符串
const element =
2.jsx表达式
const element = ;
【使用小驼峰命名】
d. Babel 会把 JSX 转译成一个名为 React.createElement() 函数调用。
const element = (
Hello, world!
);to
const element = {
type: ‘h1’,
props: {
className: ‘greeting’,
children: ‘Hello, world!’
}
};
React 通过读取这些对象,然后使用它们来构建 DOM 以及保持随时更新。
<推荐 一个在线的工具 code pen https://codepen.io/pen/ 用react的时候一定要调整setting 为Babel 且引入 //unpkg.com/react/umd/react.development.js //unpkg.com/react-dom/umd/react-dom.development.js>
a - 想要将一个 React 元素渲染到根 DOM 节点中,只需把它们一起传入 ReactDOM.render()
b - React 元素是不可变对象。一旦被创建,你就无法更改它的子元素或者属性。
c - React 只更新它需要更新的部分
function tick() {
const element = (
Hello, world!
It is {new Date().toLocaleTimeString()}.
);
ReactDOM.render(element, document.getElementById(‘root’)); // 每次更新dom id 为root的
}
setInterval(tick, 1000);
setInterval react中的Timer,nice!
b.class 组件
class Welcome extends React.Component {
render() {
return
Hello, {this.props.name}
;}
}
(二)
react元素不仅可以使DOM的标签,也可以是用户自定义的组件,突然感觉很厉害这个想法
function Welcome(props) {
return
Hello, {props.name}
;}
const element = ;
ReactDOM.render(
element,
document.getElementById(‘root’)
);
Welcome 是一个函数组件, 把name="Sara"当做一个属性参数传入 function中 实际返回的是
(三)
组件可以相互组合嵌套
function Welcome(props) {
return
Hello, {props.name}
;}
function App() {
return (
);
}
ReactDOM.render(
,
document.getElementById(‘root’)
);
(四)
所有 React 组件都必须像纯函数一样保护它们的 props 不被更改!!!!
class Clock extends React.Component {
constructor(props) { ================= 构造函数
super(props);
this.state = {date: new Date()}; ======== 设置state
}
componentDidMount() { ========= 在加入完成DOM后 会调用挂载
this.timerID = setInterval( ==== 设置Timer
() => this.tick(),
1000
);
}
componentWillUnmount() { ========== 移除DOM后 会调用卸载
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() { ======== 渲染内容
return (
Hello, world!
It is {this.state.date.toLocaleTimeString()}.
);
}
}
ReactDOM.render(
, ========== 加入到DOM中 root 下, 调用构造函数
document.getElementById(‘root’)
);
尽管 this.props 和 this.state 是 React 本身设置的,且都拥有特殊的含义,但是其实你可以向 class 中随意添加不参与数据流(比如计时器 ID)的额外字段。
(1)
不要直接修改 State 使用setState
(2)
State 的更新可能是异步的
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
传入上一次的state来更新state
HTML
Activate Lasers
React
Activate Lasers
在 React 中另一个不同点是你不能通过返回 false 的方式阻止默认行为。你必须显式的使用 preventDefault 。
function handleClick(e) {
e.preventDefault();
console.log(‘The link was clicked.’);
}
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// 为了在回调中使用 `this`,这个绑定是必不可少的, 如果不bind那么在回调的里面找不到this,这个和javascript有关系,以后可以深究 https://zh-hans.reactjs.org/docs/handling-events.html
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return (
{this.state.isToggleOn ? ‘ON’ : ‘OFF’}
);
}
}
ReactDOM.render(
,
document.getElementById(‘root’)
);
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row
<button onClick={this.deleteRow.bind(this, id)}>Delete Row
bind了this,也可以在构造函数中bind
上述两种方式是等价的,分别通过箭头函数和 Function.prototype.bind 来实现。
在这两种情况下,React 的事件对象 e 会被作为第二个参数传递。如果通过箭头函数的方式,事件对象必须显式的进行传递,而通过 bind 的方式,事件对象以及更多的参数将会被隐式的进行传递。
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
Hello!
{unreadMessages.length > 0 &&
You have {unreadMessages.length} unread messages.
}
);
}
const messages = [‘React’, ‘Re: React’, ‘Re:Re: React’];
ReactDOM.render(
,
document.getElementById(‘root’)
);
之所以能这样做,是因为在 JavaScript 中,true && expression 总是会返回 expression, 而 false && expression 总是会返回 false。
阻止条件渲染
若要完成此操作,你可以让 render 方法直接返回 null,而不进行任何渲染。
key
key 帮助 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。
function ListItem(props) {
// 正确!这里不需要指定 key:
return
- {props.value}
- ;
}function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// 正确!key 应该在数组的上下文中被指定
);
return (
{listItems}
);
}定义组件的时候不要制定Key,在数组使用的时候使用Key
class Reservation extends React.Component {
constructor(props) {
super(props);
this.state = {
isGoing: true,
numberOfGuests: 2
};this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.type === ‘checkbox’ ? target.checked : target.value;
const name = target.name;this.setState({ [name]: value });
}
render() {
return (
);
}
}受控组件
在 HTML 中,表单元素(如、 和 )之类的表单元素通常自己维护 state,并根据用户输入进行更新。而在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState()来更新。我们可以把两者结合起来,使 React 的 state 成为“唯一数据源”。
渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。控制 + 渲染
在受控组件上指定 value 的 prop 可以防止用户更改输入。如果指定了 value,但输入仍可编辑,则可能是意外地将value 设置为 undefined 或 null。
非受控组件
https://zh-hans.reactjs.org/docs/uncontrolled-components.html切记: 调用 this.setState() 进而请求 React 重新渲染自己本身。且是逐层向下
例子: 温度转换的一个例子
对应上面提到的x或y
class TemperatureInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this); 回调要bind
}handleChange(e) {
this.props.onTemperatureChange(e.target.value); 回调中调用了props参数中的回调,这个回调是上层组件传进来的
}render() {
const temperature = this.props.temperature; 渲染时也是用的上层组件传进来的参数
const scale = this.props.scale;
return (
Enter temperature in {scaleNames[scale]}:
);
}
}相当于下层组件用上层组件提供的参数和回调
参数 — 进行渲染
回调 — 更新参数,上层更新完参数,出发渲染(setstate触发)function toCelsius(fahrenheit) {
return (fahrenheit - 32) * 5 / 9;
}function toFahrenheit(celsius) {
return (celsius * 9 / 5) + 32;
}function tryConvert(temperature, convert) {
const input = parseFloat(temperature);
if (Number.isNaN(input)) {
return ‘’;
}
const output = convert(input);
const rounded = Math.round(output * 1000) / 1000; 四舍五入
return rounded.toString();
}看一下对应的z
class Calculator extends React.Component {
constructor(props) {
super(props);
this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
this.state = {temperature: ‘’, scale: ‘c’};
}handleCelsiusChange(temperature) {
this.setState({scale: ‘c’, temperature});
}handleFahrenheitChange(temperature) {
this.setState({scale: ‘f’, temperature});
}render() {
const scale = this.state.scale;
const temperature = this.state.temperature;
const celsius = scale === ‘f’ ? tryConvert(temperature, toCelsius) : temperature; 进行温度的转换
const fahrenheit = scale === ‘c’ ? tryConvert(temperature, toFahrenheit) : temperature;return ( <div> <TemperatureInput scale="c" temperature={celsius} onTemperatureChange={this.handleCelsiusChange} /> <TemperatureInput scale="f" temperature={fahrenheit} 传进去温度 onTemperatureChange={this.handleFahrenheitChange} /> 传进去回调 <BoilingVerdict celsius={parseFloat(celsius)} /> </div> );
}
}好处:
在 React 应用中,任何可变数据应当只有一个相对应的唯一“数据源”。通常,state 都是首先添加到需要渲染数据的组件中去。
然后,如果其他组件也需要这个 state,那么你可以将它提升至这些组件的最近共同父组件中。你应当依靠自上而下的数据流,而不是尝试在不同组件间同步 state。虽然提升 state 方式比双向绑定方式需要编写更多的“样板”代码,但带来的好处是,排查和隔离 bug 所需的工作量将会变少。
由于“存在”于组件中的任何 state,仅有组件自己能够修改它,因此 bug 的排查范围被大大缩减了。此外,你也可以使用自定义逻辑来拒绝或转换用户的输入。官网建议我们使用开发者工具来排错: https://github.com/facebook/react-devtools (好像已经merge到其他分支了,有空再看吧)
包含关系:
function FancyBorder(props) {
return (
<div className={‘FancyBorder FancyBorder-’ + props.color}>
{props.children} =================== 这一点可以吧任意组件传入做子组件
);
}function WelcomeDialog() {
return (
============= 1
======传入了h1 和 p 两个组件
Welcome
Thank you for visiting our spacecraft!
);
}
结合我们前面的经验: 其实我们也可以在 1 初设定id,
通过下面的方法来重新渲染
ReactDOM.render(
主键,
document.getElementById(‘root’)
);简单修改后 =>
(不推荐这样使用,个人觉得会 在结构上看不清楚,还是 {props.children} 吧)
function FancyBorder(props) {
return (
<div className={‘FancyBorder FancyBorder-’ + props.color} id = {props.id}>
);
}const componentId = “fb”;
function Content(){
return (
Welcome1
);
}function WelcomeDialog() {
var componentFB =
</FancyBorder>;
return componentFB;
}ReactDOM.render(
,
document.getElementById(‘root’)
);ReactDOM.render(
,
document.getElementById(componentId)
);例子二: 预留几个模块
function SplitPane(props) {
return (
{props.left} ====== left 组件
{props.right} ====== right 组件
);
}function App() {
return (
<SplitPane
left={
}
right={
} />
);
}特例关系
个人觉得用特许关系这个词描述的不是很恰当,不如说是组件直接的封装调用。。。可能我理解的也不对例子:
渲染Dialog组件需要上层组件传入title 和 message,同时还留了children给扩展
function Dialog(props) {
return (
{props.title}
{props.message}
{props.children}
);
}这里提一句什么时候用class 什么时候用function, 当一个组件需要控制某个变量的状态的时候,那么就需要用class,需要用state来储存
可以这么理解,静态的一些组件,比如说logo,没必要更新什么状态,function足够了,再比如登录组件,这个时候需要记录用户名,密码甚至发起ajax请求那么就需要用classclass SignUpDialog extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this); ==== 绑定this到回调上
this.handleSignUp = this.handleSignUp.bind(this);
this.state = {login: ‘’}; ==== 记录用户名
}render() {
return (
<Dialog title=“Mars Exploration Program” ========== 向dialog组件传入内容
message=“How should we refer to you?”>
<button onClick={this.handleSignUp}> Sign Me Up! </button> </Dialog> );
}
handleChange(e) {
this.setState({login: e.target.value});
}handleSignUp() {
alert(Welcome aboard, ${this.state.login}!
);
}
}官网总结:
在 Facebook,我们在成百上千个组件中使用 React。我们并没有发现需要使用继承来构建组件层次的情况。
如果你想要在组件间复用非 UI 的功能,我们建议将其提取为一个单独的 JavaScript 模块,如函数、对象或者类。组件可以直接引入(import)而无需通过 extend 继承它们。me: 合着就是你可以用继承做一个工具包,到了写页面的时候不要在用继承了!
React 哲学 (设计的一般思路)
第一步:将设计好的 UI 划分为组件层级
如果你是和设计师一起完成此任务,那么他们可能已经做过类似的工作,所以请和他们进行交流!他们的 Photoshop 的图层名称可能最终就是你编写的 React 组件的名称!
(很喜欢这种感觉)第二步:用 React 创建一个静态版本
在构建应用的静态版本时,我们需要创建一些会重用其他组件的组件,然后通过 props 传入所需的数据。props 是父组件向子组件传递数据的方式。
即使你已经熟悉了 state 的概念,也完全不应该使用 state 构建静态版本。state 代表了随时间会产生变化的数据,应当仅在实现交互时使用。所以构建应用的静态版本时,你不会用到它第三步:确定 UI state 的最小(且完整)表示
通过问自己以下三个问题,你可以逐个检查相应数据是否属于 state:- 该数据是否是由父组件通过 props 传递而来的?如果是,那它应该不是 state。 (上层管理)
- 该数据是否随时间的推移而保持不变?如果是,那它应该也不是 state。 (静态数据)
- 你能否根据其他 state 或 props 计算出该数据的值?如果是,那它也不是 state。 (可以计算的)
第四步:确定 state 放置的位置
React 中的数据流是单向的,并顺着组件层级从上往下传递
对于应用中的每一个 state:找到根据这个 state 进行渲染的所有组件。
找到他们的共同所有者(common owner)组件(在组件层级上高于所有需要该 state 的组件)。
该共同所有者组件或者比它层级更高的组件应该拥有该 state。
如果你找不到一个合适的位置来存放该 state,就可以直接创建一个新的组件来存放该 state,并将这一新组件置于高于共同所有者组件层级的位置第五步:添加反向数据流
一句话设置回调来反向修改数据终于看完了!!!!