表单
受控组件
表单中的组件显示绑定value属性,组件中状态的变化,由react组件触发change方法,最终触发本组件的setState方法,进行状态的更新 说白了,表单中用户每个一个输入的值,都会经过程序员写的方法,所以是受控组件
非受控组件
表单中输入的数据都由dom处理。 用户输入的值是不可控的,用户可以随意输入任何值。
ref
两种使用方式
函数式组件无法使用
ref属性,只有实例化组件和html组件可以使用ref
- 类似vue的用法
为组件定义ref名称
<Card ref="card" />
调用组件实例
this.refs.card
- 在组件实例化时定义
constructor(props) {
super(props);
this.card = React.createRef();
}
<Card ref={this.card} />
调用组件实例
this.card.current
组合vs继承
- react组件间的复用都采用组合的形式
- 如果组件间有函数(UI无关的功能)存在复用,建议单独提取成一个function进行复用
性能优化
PureComponent
PureComponent会为shouldComponentUpdate方法提供一个浅比较方法。如果数据没有发生变化,则不会触发render方法重新渲染。
但是如果传入的是一个对象,则每次都不会渲染,因为对象的比较是指针。
所以不使用突变改变对象,使用concat,Object.assign,展开运算符等能返回一个新对象的方法进行赋值修改,或者可以使用immutable与PureComponent结合使用。
diff-keys
当react渲染列表时,需要为每一个相同的子组件添加一个key属性。 如果没有key属性的话,在列表的头部插入一个节点,那么react会认为整个列表都需要重新渲染,其实只需要在头部渲染那个新增的节点
增加了key属性以后,react会使用key来匹配原始树的节点,如果原始的key还存在react并不会销毁该组件,只会移动或者修改
key必须是唯一的
必须要保证每一条数据的key都是唯一的,因为如果不唯一,可能会导致子组件数据更新了,然后触发componentWillReceiveProps方法去更新其子组件 但是如果其子组件的状态是通过子组件自身的state维护,而非通过props进行维护,例如为非受控组件,就会导致该组件无法更新的情况
context
原来嵌套组件传值通过props一层一层传进去
现在通过context可以解决这种高度耦合的问题
const {Provider, Consumer} = React.createContext(defaultValue);
父组件设为<Provider value={/* some value */}>可以用来传递参数,子组件设为<Consumer> {value => /* render something based on the context value */} </Consumer>可以用来接收参数
Portals
ReactDOM.createPortal(child, container)
可以将子元素,渲染到指定dom节点,而不是默认的父节点中。
虽然不在父节点中,但是父节点可以监听到改元素的冒泡时间
HOC
高阶组件,用来将一些组件中相同的功能抽象出来,封装成一个函数,不同的部分作为组件
当使用高阶组件时,如果直接在高阶组件上使用ref,获取到的值,为高阶组件的ref而不是我们想获取到的,被封装的组件的ref。
Hooks
什么是hook?
Hook是一种特殊功能,可让您“挂钩”React功能。例如,useState是一个Hook,它允许您将React状态添加到功能组件。我们稍后会学习其他的Hook。
我什么时候使用hook?
如果你编写一个函数组件并意识到你需要为它添加一些状态,那么之前你必须将它转换为一个类。**现在,您可以在现有功能组件中使用Hook。**我们现在要做到这一点!
useState
使无状态组件可以使用类的功能
const [count, setCount] = useState(0);返回当前状态和更新它的函数
useEffect
使无状态组件可以使用声明周期功能
可以取代下面这三个生命周期的功能 componentDidMount, componentDidUpdate, and componentWillUnmount
组件更新时触发
正常情况下,初始化会触发一次useEffect,只要组件更新就会触发useEffect
React.useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
});
特定值发生变化时触发
当不需要每次组件状态更新都触发时,在尾部传入数组可以设置需要监听的值
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [ count ]);
仅初始化时触发
如果数组中什么值都不传入,那就只会在初始化时触发一次
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [ ]);
仅在组件销毁时触发
数组中不传入任何值,并且在钩子中return一个函数
useEffect(() => {
return () => {
console.log('delete');
};
}, [ ]);
自定义hook
自定义挂钩是一种自然遵循Hooks设计的约定,而不是React功能
将hook封装在一个函数中,使其具有复用性
import React, { useState, useEffect } from 'react';
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
});
return isOnline;
}
钩子使用注意事项
在组件顶层使用useState声明钩子,不要在if语句中声明
prop-types
用来设置组件的接受的props,以及一些限制
component.propTypes = {
onClick: PropTypes.func.isRequired,
completed: PropTypes.bool.isRequired,
text: PropTypes.string.isRequired
}
redux
action
定义了动作的类型,以及每个动作所需要的参数
export function addTodo(text) {
return { type: ADD_TODO, text }
}
reducers
使用switch...case匹配action类型的动作,修改相对应的state
function visibilityFilter(state = initialState.visibilityFilter, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return action.filter; //返回的值即为更新的后的state
default:
return state;
}
}
使用combineReducers封装多个reducer
const todoApp = combineReducers({
visibilityFilter,
todos,
articleList
});
store
在前面的章节中,我们学会了使用 action 来描述“发生了什么”,和使用 reducers 来根据 action 更新 state 的用法。
Store 就是把它们联系到一起的对象。Store 有以下职责:
- 维持应用的
state; - 提供
getState()方法获取state; - 提供
dispatch(action)方法更新state; - 通过
subscribe(listener)注册监听器; - 通过
subscribe(listener)返回的函数注销监听器。