Open dlrandy opened 6 years ago
渲染多个元素不需要封装在组件的一个公共父元素里了 React16之前要求render方法只能返回一个react元素。16以后是可以返回一个元素 数组的。列表的key不能省略,但是多个组件返回的是同一类的列表元素,key是可以相同的
const data = [
[14, 24, 125, 182],
[10, 122, 220, 310],
[0, 19, 38, 50]
];
const Aux = props => {
return props.children;
};
const Fruits = () => [
<li key="1">Apple</li>
<li key="2">Orange</li>
<li key="3">Banana</li>
];
class MoreFruits extends React.Component {
render(){
return [
<li key="1">Strawberry</li>,
<li key="2">Blueberry</li>
]
}
}
class App extends React.Component {
render(){
return (
<div>
<ul>
<li>Peach</li>
<li>Ananas</li>
<Fruits />
<MoreFruits />
</ul>
<table>
<tbody>
<RawRows data={data} />
<StatsRows data={data} />
</tbody>
</table>
</div>
);
}
}
let idCounter = 0;
const uniqueId = () => ++idCounter + '';
const convertRowsToColumns = data => (
data[0].map( (col, i) => data.map(row => row[i]))
);
const sumPerColumn = data => (
convertRowsToColumns(data).map(cols => cols.reduce((sum, col) => {
return sum + col;
}, 0))
);
const trendInPercent = data =>
sumPerColumn(data).map((value, index, list) => {
if (index === 0) {
return 0;
}
return (100 / list[index - 1] * value - 100).toFixed(0);
});
const RawRows = props =>
props.data.map(row => (
<tr key={uniqueId()}>
{row.map(item => <td key={uniqueId()}>{item}</td>)}
</tr>
));
const SumRow = props => (
<tr key="1" style={{ borderTop: '2px solid #333' }}>
{sumPerColumn(props.data).map(value => <td key={uniqueId()}>{value}</td>)}
</tr>
);
const TrendRow = props => (
<tr key="2">
{trendInPercent(props.data).map(value => (
<td key={uniqueId()}>{value}%</td>
))}
</tr>
);
const StatsRows = props => (
<Aux>
<SumRow data={props.data} />
<TrendRow data={props.data} />
</Aux>
);
ReactDOM.render(<App />, document.getElementById('root'));
react16之前只渲染文本的组件需要封装在span或者div里。react16移除了这种不需要的结构
const Comment = ({ text }) => {
const emojifiedText = text
.replace(':)', '😊')
.replace(':D', '😀')
.replace(':(', '🙁');
return emojifiedText;
};
class App extends React.Component {
render() {
return (
<div>
<Comment text="Today we are sailing home :)" />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
React16使用Portals
默认React组件树直接映射DOM树。但是有一些情况,UI元素像overlay或者loading spinner 这种就有局限性了。React6配备了portal特性,允许在不同的根元素里追加部分组件树
border 和outline很相似不同在于: 1、虽然它们都是画在元素内容content的外面,在outline不占用空间 2、outline可以是非矩形的
ReactDOM 提供了一个createPortal方法,接收两个参数一个新的子树容器组件,第二个是目标容器
class Overlay extends React.Component {
constructor(props) {
super(props);
this.overlayContainer = document.createElement('div');
document.body.appendChild(this.overlayContainer);
}
componentWillUnmount() {
document.body.removeChild(this.overlayContainer);
}
render() {
return ReactDOM.createPortal(
<div className="overlay">
<span onClick={this.props.onClose}>X</span>
{this.props.children}
</div>,
this.overlayContainer
);
}
}
class App extends React.Component {
constructor(props){
super(props);
this.state = {
overlayActive: true
};
}
closeOverlay = () => {
this.setState({
overlayActive: false
});
}
render() {
return (
<div>
<h1>Dashboard</h1>
{
this.state.overlayActive &&
<Overlay onClose={this.closeOverlay}>
<div>Welcome</div>
</Overlay>
}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
.overlay {
position: fixed;
top: 50%;
left: 50%;
width: 260px;
line-height: 200px;
height: 200px;
margin-left: -130px;
margin-top: -100px;
background-color: #fff;
text-align: center;
outline: 9999px solid rgba(0, 0, 0, 0.5);
}
.overlay div {
display: inline;
}
<!doctype html>
<html>
<head>
<script src="https://unpkg.com/react@16.0.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.0.0/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6.26.0/babel.min.js"></script>
<link rel="stylesheet" href="index.css">
</head>
<body>
<div id="root"></div>
<script type="text/jsx" src="script.jsx"></script>
</body>
</html>
React16带来了一些DOM组件(div)属性的改变 1、添加自定义属性 2、可以直接将html拷贝到React的render方法里,但是class属性仍需要使用className 3、对于属性只能是字符串值或是能转成String的对象,事件句柄必须是函数
setState传递null不会触发更新。也就是可以在setState的updater方法里来决定是否更新。
给一个组件添加样式,可以使用className,style属性接收对象,但是这个对象没有厂商前缀,需要自己加。内置样式也会有一些问题。所以可以用一些库Popular libraries include styled components, emotion, and glamorous
react组件从props初始化state几种方式: 1、直接在constructor里初始化 2、在componentDidMount里设置state//个人不推荐使用 3、使用public class fields的时候直接读取props,其实和constructor一样
React 使用事件代理,整个document里每个类型的事件真的只有一个事件句柄,他负责管理调用你的事件句柄
react组件事件处理器的三种写法: 1、内置箭头函数或者使用了bind的函数//这个有性能影响 2、在constructor里使用bind进行绑定 3、使用public class fields:使用bind或者箭头函数 // 和默认constructor一样的情况下可以省略constructor和this
ref在自定义组件上引用的是实例(this),注意若是函数组件则不起作用;dom元素上是DOM; 注意一下父亲组件访问子组件dom元素的情况
react里form的onSubmit的event,里访问元素的方式: 1、event.target[n].value 2、event.target.elements.username.value 3、使用ref 注意 form === event.target
react的数组元素为什么需要key,是因为在重新渲染的时候,他需要比较新数组和旧数组,为了知道哪些选项被移除或者添加,需要使用key进行追踪。例如有一个div列表,div选项里有truthelement和一个orderedelement,如果div列表没有给div分配key,移除truthelement的时候,不一定会正确的移除orderedelement。还有就是列表里选项各自互相自动交换的时候,focus的的元素不会失去focus,,交换之后仍有focus
component
pureCompoennt
stateless compoennt( react15里是PureComponent , 16里就不是了,以后可能会变成Pure的, slow所以除了下面的情况,尽量不要用了 ) 适用于较小的rerender的时候cost可以忽略的;还有就是event propagation boundraies(这个我也不理解碰到再说);他还用在Component Adapter做一些检查
PureComponents 是指绝对相同的input将会产生绝对相同的output(whole nesting component tree)
作为一个PureComponent,要考虑shouldUpdateYourSelf, 还要考虑shouldUpdateAllYourChildren
pure 函数不是 PureCompoennt 在使用PureComponent的时候,不但要考虑props,state,还要考虑Context,因为PureComponent会阻塞Context的 change,但是这只是React15的问题,16的话就没有这个问题了,context的Api变了
所以一般使用PurreComonent考虑整个的Nesting Component而不是单个的组件。用作原子组件还是很安全的,但是分子组件就要小心了. 如果用于分子组件,可能要使用EventEmitter之类的
https://medium.com/@antonkorzunov/2-things-about-purecomponent-you-probable-should-know-b04844a90d4
https://hackernoon.com/restate-the-story-of-redux-tree-27d8c5d1040a https://medium.com/groww-engineering/stateless-component-vs-pure-component-d2af88a1200b
这段代码的错误是什么哦?
import React from "react";
import ReactDOM from "react-dom";
let TestElement = {
$$typeof: Symbol.for("react.forward_ref"),
render: (props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>)
};
const ref = React.createRef();
ReactDOM.render(<TestElement ref={ref} >hhdat</TestElement>, document.getElementById("app"));
Component Instance Element Component 是UI的声明类; Instance是Component的实例, 不同于传统的UIComponent, 他有自己的生命周期,不需要开发者手动处理子组件的创建和销毁等; Element是描述Instance或者DOM及其属性的普通对象。包括组建类型,属性和子元素。他只是告诉React你想在屏幕上看到什么 { type: '', props: { }, $$typeof: 'React_element'//处于安全的原因, render(){} } type是字符串的时候,是普通的Dom元素;当是函数或者类的时候,则是组件元素。 描述组件的元素也是元素。 Reactelement还有一个额外的$$typeof属性(处于安全的原因 XSS),这些Element有的加上$$typeof可以正常使用,有些则要使用react.createElement或者jsx重写才能正常渲染。
描述组件的元素(Element)也是元素
const DangerButton = ({ children }) => ({
type: Button,
props: {
color: 'red',
children: children,
}
});
const DeleteAccount = () => (
<div>
<p>Are you sure?</p>
<DangerButton>Yep</DangerButton>
<Button color='blue'>Cancel</Button>
</div>
);
React 的元素树可以包含DOM节点的Element还可以包含描述其他组件的Elements,这样可以不依赖DOM结构组建UI。 React负责管理组件实例的
注意创建元素使用React.createElement JSX React.createFactory. 不要直接用普通的对象写Elements。只需要知道他们底层都是普通的对象。
React Elements是当React运行它的reconciliation过程的时候,将要被i渲染的对象
组件类型的React Elements会在reconciliation的时候完全的渲染成string类型的ReactElements
高阶组件工厂:componentA => ComponentB 高阶组件工厂的工厂:props => ComponentA => ComponentB
作用是 1、属性代理:就是处理传递给wrappedComponent的Props 1'处理属性 2‘通过Ref访问实例 3’state抽离 WrappedComponent不需要拥有自己的state 4‘包装WrappedComponen在其他的元素里
2、继承反转:新组件继承wrappedComponent 这个允许HOC 通过this访问WrappedComponent 实例,意味着可以访问state,props,component lifecycle hooks和render方法, 同时也能创建WrappedComponent的新的lifeCycle hooks. 继承反转的HOC不能保证整个children tree解析,Render Highjacking能够处理WrappedComponent render的Element tree 但是如果element tree里有函数组件的话,是不能够处理该组件的孩子的
1‘Render Highjacking
Render Highjacking是因为HOC控制着WrappedComponent的render输出:
注意Render highjacking和Props Proxy的区别,他们是不能一起使用的 2’ Manipulating state
Manipulaing state: HOC可以读取和编辑,删除WrappedComponent实例的state,(为什么可以这么做?是因为当前的this就是WraapedComponet Instance),而且你也可以加入更多的state。这种方式容易弄乱WraapedComponent 实例的state, 所以一般这种情况只限于读取和增加state,对于增加state的情况最好加上namespace, 不要搞乱了wrappedComponent
ReactHOC主要是为了复用组件的逻辑
https://reactjs.org/docs/higher-order-components.html#___gatsby HOC的陷阱 1、不要在render方法里 使用HOCS reconciliation也就是react的diff算法,他检查component identity决定是否应该更新子树或者抛弃旧新建新的。使用的情况下 每次render都会新创建tree,就会重新挂载组件引起组件的state和children的丢失。 2、static的方法一定要拷贝,手动拷贝或者使用hoist-non-react-statics 3、Refs不能被传递,因为ref不是真正的prop,他引用的是外层组件的实例而不是wrappedComonent的 解决的办法是使用forwardRef, 他产生新的组件,是一个高阶组件. Ref forwarding就是让某些组件接收ref然后转发给child
https://medium.com/@franleplant/react-higher-order-components-in-depth-cf9032ee6c3e
React 的render方法创建React Element tree。再下一次的state或者props更新的时候,render会返回一个不同的React Element tree。接下来 React需要知道两个对象的不同,来高效的更新UI匹配最新的element tree。
react的启发式算法是基于两个假设: 1、不同type的元素产生不同的tree 2、开发者可以使用key提示哪些子元素是在不同的render下 是稳定的
React的Diff算法 1、比较elment的type,当然是从根元素起 比较,旧tree销毁新tree重建 2、同类型的DOM元素,比较两者的attribute,更新必要的attributes 3、同类型的Component 元素,instance不变,维护state,React会更新instance的props来匹配新的元素,最后调用render然后再diiff两个tree 4、在children上递归 5、keys
Why use immutable in reactjs? 因为我们需要比较数据的变化,来决定是否需要调用setState的,因为state变化是要组件树重新渲染的 ,还有就是合并数据的时候,手动的操作也会很复杂。当你的数据很复杂的时候(React的最佳实践是要state最小),这个比较是很复杂的。
immutable的意思就是当有一个对象需要改变的时候,不要对其本身进行mutate,而是创建一个新的changed 拷贝。 下面是原生的immutable方法
{...}
Object.assign
[...]
concat
但是每一次的改变都要生成一个新对象是很占用内存的,所以immutablejs使用了持久化数据结构和结构共享的技术 https://medium.com/@dtinth/immutable-js-persistent-data-structures-and-structural-sharing-6d163fbd73d2
React的Async update
react将要移出的三个生命周期函数(因为不安全的coding 实践) componentWillMount/componentWillUpdate/comopnentWillReceiveProps
react16.3里引入的两个新的生命周期 static的getDerivedStateFromProps和getSnapshotBeforeUpdate getDerivedStateFromProps 是在组件实例化之后以及re-render之前。它可以返回一个对象更新state,或者null来表明新的Props不需要做任何的state更新。它和comonentDidUpdate一起使用来替换掉componentWillReceiveProps的用例。
PLACEHOLDER
getSnapshotBeforeUpdate生命周期在mutation(DOM改变之前掉用)。它的返回值将传递给comonentDidUpdate的第三个参数。它和componentDidUpudate完全可以cover compnentWillUpdate的用例。
在componentWillMount获取外部数据,在 server rendering 和即将到来的async rendering mode都会有问题。第一种请求回来的数据不会被使用;第二种的request可能被发送多次。
在componentWillMount里监听事件易造成内存泄露,因为server渲染得时候compnentWillUnmount调用不到的。async render的时候render可能会被中断,导致compnentWillUnMount不会被调用
只有compoenntDidMount被调用了才能保证componentWillUnmount被调用
getDerivedStateFromProps的 反模式: 1、无条件的copy props到state 2、当有props变化的时候,擦除state
替换的方案有: 1、Fully controlled component完全受控的组件 2、Fully uncontrolled component with a key带有key完全不受控的组件
uncontrolled components是数据由 DOM自己操纵; controlled components是数据由React Component操纵;
React的Async render 就是 render方法里获取数据暂停rendering, 这种的需要fallback,可以使用Tiemout组件作为fallback 。 'drum-roll' https://medium.com/@DidierFranc/when-react-has-become-even-more-asynchronous-37a55c3a3d3
Here's how suspending works:
https://news.ycombinator.com/item?id=16492973
https://edgecoders.com/react-16-features-and-fiber-explanation-e779544bb1b7
react16的新能力: 1、把可中断的任务划分成chunk 2、在progress中划分优先级,染合,复用任务 3、父子切换来支持layout 4、render渲染多个元素的能力 5、Portals 6、error boundary
新特性 1、核心算法重写,重写的最主要特性就是async rendering async rendering是指rendering工作可以被划分成chunks,分布到多个frames
手机上的一个click会触发哪些事件? touchstart touchmove touchend mouseover mousemove mousedown mouseup click 奇怪了手机上我用jq监听的是mouseenter啊?为啥事件触发了? 因为jquery使用的mouseover + event-time check代理的mouseenter。
https://www.html5rocks.com/en/mobile/touchandmouse/ https://github.com/jquery/jquery/blob/master/src/event.js
mouseenter 和mouseover 有啥区别阿? 前者不冒泡;
mouseleave和mouseout的却别? 前者不冒泡。
使用ErrorBounaries进行错误处理 UI里的js错误不该破坏掉整个app。ErrorBoundaries就是一个react组件,来catch子组件树里的jserror的,它可以记录errors,显示fallbackUI,而不会让整个的组件树崩溃
React 16加了一个新的生命周期钩子componentDidCatch(error, componentStack)来处理封装的组件抛出来的errors,而不会卸载整个的app.这个钩子在production环境的时候,可以用来给server发送error报告。
componentDidCatch的几个特点: 1、它捕获子组件树上任意位置的jserror,记录Error,当组件崩溃的时候渲染fallback UI 2、可以封装在一个独立的组件进行复用 3、因为Errorboundaries作用于深度内嵌的结构,所以最好把他放在有意义的位置,而不是每一级 4、任何的功能组件或者类组件的render方法,构造器方法以及生命周期方法里。注意任何的Eventhandler方法抛出的错误不会被钩子捕获,除非它是在setState的updater函数里。
总结一下就是react组件的方法报错都好使,事件处理句柄只能是setState的方法里有错误的时候好使