在React conference 2018上, React团队指出了React社区中被广泛关注的问题:
(1) 逻辑复用问题: react中,组件是构建应用的基本组成部分, 为了组件更好地复用,目前存在两种方案: 高阶组件(HOC) 和Render props,这些方案都有不同的适用场景,但是也带来了明显的缺点,如组件地狱
(2)巨型组件(Giant components):组件中各种的生命周期函数,但是组件的逻辑是相似的,如componentDidMount和ComponentDidUpdate的时候都可能存在发送ajax请求 或者是相反的, 如componentDidMount和componentWillUnMount会添加/清除绑定的事件, 计时器等
(3)class component还是functional component
代码演示
链接
1. [state, useState]
class Greeting extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'Allen'
}
this.handleNameChange = this.handleNameChange.bind(this);
}
handleNameChange(e) {
this.setState({
name: e.target.value
})
}
render() {
return (
)
}
}
ReactDOM.render(
,
document.querySelector('#root')
)
将class component换成 functional component是这样的, 是不是简洁了好多!
function Greeting(props) {
const [name, setName] = React.useState('Marry')
function handleNameChange(e) {
setName(e.target.value);
}
return (
)
}
注意这里的
const [name, setName] = React.useState('Marry'); // resource为值, setResource为设置值的函数, []里面的命名可以任意
2. useContext
const locale = React.createContext('English');
{LocaleContext => (
{LocaleContext}
)}
LocaleContext.Consumer>
使用useContext进行替换
let LocalContext = React.createContext('English')
const locale = React.useContext(LocaleContext);
{locale}
3.useEffect
假设现在需要体添加一个myName的div来实时显示当前的名称, 我们可以使用componentDidMount和componentDidUpdate的方法
this.state = {
name: 'Allen',
myName: '',
}
componentDidMount() {
this.state.myName = this.state.name;
}
componentDidUpdate() {
this.state.myName = this.state.name;
}
{this.state.myName}
使用useEffect方法
const [myName, setMyName] = React.useState('');
React.useEffect(() => {
setMyName(name);
})
看完上面的api你可能会认为, hooks就是为了让functional component具有state和lifecycle method, 实际上并不完全是,hooks的更大用处在于代码的复用
假设我们有这样一个useEffect的逻辑, 接受一个resource参数, 返回一个resource array, 我们提取出一个useResource函数
import { useState, useEffect } from 'react';
import axios from 'axios';
const useResources = resource => {
const [resources, setResources] = useState([]);
useEffect(
() => {
(async resource => {
const response = await axios.get(
`https://jsonplaceholder.typicode.com/${resource}`
);
setResources(response.data);
})(resource);
},
[resource]
);
return resources;
};
export default useResources;
引用
import React from 'react';
import useResources from './useResources';
const UserList = () => {
const users = useResources('users');
return (
{users.map(user => (
- {user.name}
))}
);
};
export default UserList;
另外一个代码复用的例子:
class App extends React.Component {
state = { lat: null, errorMessage: '' };
componentDidMount() {
window.navigator.geolocation.getCurrentPosition(
position => this.setState({ lat: position.coords.latitude }),
err => this.setState({ errorMessage: err.message })
);
}
renderContent() {
if (this.state.errorMessage && !this.state.lat) {
return Error: {this.state.errorMessage};
}
if (!this.state.errorMessage && this.state.lat) {
return ;
}
return ;
}
render() {
return {this.renderContent()};
}
}
获取地理位置的函数可以单独抽取出来useLocation.js
import { useState, useEffect } from 'react';
export default () => {
const [lat, setLat] = useState(null);
const [errorMessage, setErrorMessage] = useState('');
useEffect(() => {
window.navigator.geolocation.getCurrentPosition(
position => setLat(position.coords.latitude),
err => setErrorMessage(err.message)
);
}, []);
return [lat, errorMessage]; //注意这里的返回值
};
在App.js中
import useLocation from './useLocation';
const App = () => {
const [lat, errorMessage] = useLocation() // 一行代码
const renderContent = () => {
if (errorMessage && !lat) {
return Error: {errorMessage};
};
if (!errorMessage && lat) {
return ;
};
return ;
}
return {renderContent()};
}