跳到主要内容

基础

组件

React组件包括class组件和函数组件。

实时编辑器
结果
Loading...
实时编辑器
结果
Loading...

当组件内部state改变或props地址改变的时候组件会重新渲染,并且对于非纯组件来说,父组件的渲染也会引起子组件的渲染。

我们可以使用React.PureComponentReact.memo实现纯组件。

class Akara extends React.PureComponent { // 纯类组件
// ...
}

const App = React.memo(() => <div>akara</div>) // 纯函数组件

状态

在类组件中通过this.setState修改数据。

this.setState({name: 'aka'})
this.state = Object.assign(this.state, { name: 'aka' }) // 简单地理解

通常状态的更新是异步的。

this.setState({
count: 1 // 假设原始值为0
})
console.log(this.state.count) // 输出0而不是1

想要获取修改后的值,我们可以传一个回调函数给setState

this.setState({
count: 1
}, () => {
console.log(this.state.count) // 输出1
})

如果setState依赖之前的State,setState的参数可以为函数

this.setState((state, props) => {
return {
count: state.count + 1
}
})

事实上,在合成事件组件的生命周期setState是异步的;而在原生事件定时器setState是同步的。

这是因为,React内部维护了一个标识:isBatchingUpdates。在合成事件组件的生命周期中,该值为true,那么setState会被缓存进队列,最后才批量更新;而在原生事件定时器中,该值为false,调用setState时会直接同步更新。

事件

React中的event合成事件,我们无需担心任何浏览器的兼容性问题。

<button onClick={this.handleClick}>
aka
</button>

我们有几种方式绑定事件。

// 方法一
constructor() {
this.handlerClick = this.handlerClick.bind(this)
}

// 方法二
<button onClick={(e) => this.handleClick(e)}>
Click me
</button>

// 方法三
class Btn extends React.component {
handlerClick = (e) => {
console.log(this)
}

render() {
return (
<button onClick={this.handlerClick}>
Click me
</button>
)
}
}

生命周期

详细的生命周期可以参考该链接

Form

React把表单元素分为受控组件非受控组件,默认的input元素被视为非受控组件,此时我们通常会给defaultValue属性;当我们给input元素加上valueonChange属性时该元素被视为受控组件,此时input元素的内部值完全由我们的状态控制。

// 受控组件
handleChange = (e) => {
this.setState({
name: e.target.value.toUpperCase()
})
}

render() {
return <input type="text" value={name} onChange={this.handleChange}/>
}

Ref and forwardRef

使用现代框架的一大特点是基于数据驱动的页面渲染,避免了复杂的DOM操作。有的时候我们就是想使用DOM,此时可以使用ref

class App extends React.Component {
constructor(props) {
super(props)
this.myRef = React.createRef()
}

handleClick = () => {
this.myRef.current.focus()
}

render() {
return (
<div>
<input type="text" ref={this.myRef} />
<button onClick={this.handleClick}>点我</button>
</div>
)
}
}

对于一个FancyButton组件,我们或许希望能用ref拿到组件内部的元素,此时可以使用Refs转发

const FancyButton = React.forwardRef((props, ref) => {
return <button ref={ref}>aka</button>
})

<FancyButton ref={myRef}></FancyButton>

Context

Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言。

类组件

const ThemeContext = React.createContext("akara");
export const MyProvider = ThemeContext.Provider;

const App = () => {
return (
<div>
<Test />
</div>
);
};

class Test extends React.Component {
static contextType = ThemeContext
render() {
return <div>{this.context}</div>
}
}

函数式组件

const ThemeContext = React.createContext("akara");
export const MyProvider = ThemeContext.Provider;

export function useMyContext() {
const context = useContext(ThemeContext);
return context;
}

const App = () => {
return (
<div>
<MyProvider value="bkara">
<Test />
</MyProvider>
</div>
);
};

const Test = () => {
const context = useMyContext();
return <div>{context}</div>;
};

Fragment

function App() {
return (
<>
<React.Fragment>
<div></div>
<div></div>
</React.Fragment>
</>
)
}

Hight-Order Components

高阶组件是参数为组件,返回值为新组件的函数。

// 官网例子
function App () {
const MouseWithCat = withMouse(Cat)
return (
<MouseWithCat />
)
}

function Cat (props) {
let {x, y} = props.mouse
x += 20
y += 20
return (
<img src='https://messiahhh.github.io/blog/logo.png' style={{position: 'absolute', left: x , top: y, width: '40px', height: '40px'}}/>
)
}

function withMouse(WrappedComponent) {
return function () {
let [point, setPoint] = useState({
x: 0,
y: 0,
})

const move = (e) => {
setPoint({
x: e.clientX,
y: e.clientY
})
}

return (
<div style={{height: '100vh'}} onMouseMove={move}>
鼠标的位置:{ point.x } , { point.y }
<WrappedComponent mouse={point} />
</div>
)
}
}

Render Props

// 官网例子
function App () {
return (
<Mouse render={point =>
<Cat mouse={point} />
}/>
)
}

function Cat (props) {
let {x, y} = props.mouse
x += 20
y += 20
return (
<img src='https://messiahhh.github.io/blog/logo.png' style={{position: 'absolute', left: x , top: y, width: '40px', height: '40px'}}/>
)
}


function Mouse(props) {
let [point, setPoint] = useState({
x: 0,
y: 0,
})

const move = (e) => {
setPoint({
x: e.clientX,
y: e.clientY
})
}

return (
<div style={{height: '100vh'}} onMouseMove={move}>
鼠标的位置:{ point.x } , { point.y }
{props.render(point)}
</div>
)
}

Error Boundary

当某个组件抛出异常时,开发模式下能在页面看到错误信息,而生产模式下整个页面都白屏了。在生产模式下为了避免某个组件的错误让整个应用都塌陷,我们可以使用Error Boundary组件来捕捉和打印组件内部的异常信息,并提供页面的降级处理。

一个类组件被视为错误边界组件的前提是我们给该组件定义了static getDerivedStateFromError() componentDidCatch(),通常我们会同时提供这两个方法。static getDerivedStateFromError() 的作用是当出现异常时提供降级UI,componentDidCatch()内部通常用来打印错误日志。

class ErrorBoundary extends React.Component {
constructor(props) {
super(props)
this.state = {
hasError: false
}
}

static getDerivedStateFromError(error) {
return {
hasError: true
}
}

componentDidCatch(error, errorInfo) {
console.log(error, errorInfo);
}

render() {
if (this.state.hasError) {
return <div>i'm fallback UI</div>
}
return this.props.children
}
}

const App = () => {
const [count, setCount] = useState(0)
const handleClick = useCallback(() => {
setCount(count + 1)
}, [count])

if (count >= 5) {
throw new Error('i crashed!')
}
return (
<>
{count}
<div onClick={handleClick}>点击</div>
</>
);
};

ReactDOM.render(
<ErrorBoundary>
<App />
</ErrorBoundary>,
document.querySelector("#root")
);

Portals

const App = () => {
return (
<>
<div>hello akara</div>
<Modal>
<div>i'm modal</div>
</Modal>
</>
);
};

const Modal = (props) => {
return ReactDOM.createPortal(
props.children,
document.querySelector('body')
)
}