在react router项目中,有这样的一个需求,首先展示用户名列表,点击某个用户名后,根据用户名在后台取得用户具体信息在详情页进行展示。
此时可以将详情页封装成一个组件,利用react router将userId传递给详情页组件,详情页组件向后台请求数据,然后进行展示。
使用component时,详情页组件代码如下
import {PureComponent, Component} from "react";
import React from "react";
class ComponentUser extends PureComponent {
constructor(props) {
super(props);
this.state = {}
}
getUserId = () => {
const {
match: {
params: {id},
},
} = this.props;
return id;
};
fetchContent = (userId) => {
console.log("fetch");
this.setState({content: "loading"});
new Promise(resolve => {
setTimeout(() => {
resolve("content of " + userId)
}, 2000)
}).then(content => {
this.setState({content})
})
};
componentDidMount() {
const userId = this.getUserId();
this.fetchContent(userId);
}
render() {
const userId = this.getUserId();
const {content} = this.state;
return (
<div>
<div>{userId}</div>
<div>{content}</div>
</div>
)
}
}
export default ComponentUser
列表页代码如下
import React from "react";
import {BrowserRouter as Router, Route, Link} from "react-router-dom";
import RenderUser from './RenderUser'
import ComponentUser from './ComponentUser'
const App = () => (
<Router>
<div>
<nav>
<ul>
<li>
<Link to="/componentUser/user1">userA</Link>
</li>
<li>
<Link to="/componentUser/user2">userB</Link>
</li>
</ul>
</nav>
<Route path="/componentUser/:id" component={ComponentUser}/>
</div>
</Router>
);
export default App;
不过此时会有个问题,切换点击切换userA和userB的时候,发现页面并没有更新,这是由component属性的性质决定的,react会进行组件复用。
所以需要在组件中添加componentDidUpdate函数,期望在userId发生变化后重新获取数据。
import {PureComponent, Component} from "react";
import React from "react";
class ComponentUser extends PureComponent {
constructor(props) {
super(props);
this.state = {}
}
getUserId = () => {
const {
match: {
params: {id},
},
} = this.props;
return id;
};
fetchContent = (userId) => {
console.log("fetch");
this.setState({content: "loading"});
new Promise(resolve => {
setTimeout(() => {
resolve("content of " + userId)
}, 2000)
}).then(content => {
this.setState({content})
})
};
componentDidMount() {
const userId = this.getUserId();
this.fetchContent(userId);
}
componentDidUpdate(prevProps) {
const userId = this.getUserId();
const {
match: {
params: {id: previousUserId},
},
} = prevProps;
if (userId !== previousUserId) {
this.fetchContent(userId);
}
}
render() {
const userId = this.getUserId();
const {content} = this.state;
return (
<div>
<div>{userId}</div>
<div>{content}</div>
</div>
)
}
}
export default ComponentUser
这里要注意的是,componentDidUpdate中需要判断当前的userId是否和原来的userId一致,只有不一致的时候才需要重新获取数据。不这样做的话,会导致无限循环的setState和componentDidUpdate。
使用render则可以减少三分之一的代码行数,此时详情页组件代码如下
import {PureComponent} from "react";
import React from "react";
class RenderUser extends PureComponent {
constructor() {
super();
this.state = {}
}
componentDidMount() {
console.log("fetch");
this.setState({content: "loading"});
const {userId} = this.props;
new Promise(resolve => {
setTimeout(() => {
resolve("content of " + userId)
}, 1000)
}).then(content => {
this.setState({content})
})
}
render() {
const {userId} = this.props;
const {content} = this.state;
return (
<div>
<div>{userId}</div>
<div>{content}</div>
</div>
)
}
}
export default RenderUser
列表页代码如下
import React from "react";
import {BrowserRouter as Router, Route, Link} from "react-router-dom";
import RenderUser from './RenderUser'
import ComponentUser from './ComponentUser'
const App = () => (
<Router>
<div>
<nav>
<ul>
<li>
<Link to="/renderUser/user1">user1</Link>
</li>
<li>
<Link to="/renderUser/user2">user2</Link>
</li>
</ul>
</nav>
<Route path="/renderUser/:id" render={
({match}) => (<RenderUser key={match.params.id} userId={match.params.id}/>)
}/>
</div>
</Router>
);
export default App;
可以看到由于将userId作为组件的key,可以避免组件复用,从而降低代码的复杂程度。