React:动态路由未正确重新渲染
React: Dynamic route not re-rendering correctly
通过 react router 4
了解动态路由,同时决定将功能组件重构为 class 组件。这背后的原因,如果我错了请纠正我,是 TopNavSub
组件从 react router 4
从 this.props.match.params.<var>
获取信息并选择要呈现的内容。
对我来说,这听起来像是状态的改变。唯一让我怀疑的是状态永远不会在同一条路线上改变并且每条路线总是不同所以我不确定这是否与处理状态相同。例如,/mac
将仅映射 mac
数组中的项目等
我被告知直接在 this.state
中使用 this.props
是一个糟糕的选择,而是使用 componentWillReceiveProps()
但 React 团队似乎对此感觉不佳,UNSAFE_componentWillReceiveProps(),但它什么也没解决。
TLDR;
所以这里的问题是,重构后,我的数据只显示第一个初始路由,之后无论选择哪条路由,除非刷新,否则只会显示初始选择的路由数据(f5
)。
TopNavSub.js
import React, { Component } from 'react';
// data
import { subNavLinks } from './navigationData';
class TopNavSub extends Component {
constructor(props) {
super(props);
this.state = {
navLinks : { ...subNavLinks },
product : this.props.match.params.product,
currentProduct : Object.keys(subNavLinks).filter(subNavLinksKey => subNavLinksKey === this.props.match.params.product),
}
}
render() {
return (
(!this.state.navLinks)
?
null
:
<ul>
{
this.state.navLinks[this.state.product].map(product => {
return (
<li key={ product }>{ product }</li>
)
})
}
</ul>
)
}
};
export default TopNavSub;
navigationData.js
export const subNavLinks = {
mac: ['MacBook', 'MacBook Air', 'MacBook Pro', 'iMac', 'iMac Pro', 'Mac Pro', 'Mac Mini', 'Accessories', 'High Sierra', 'Compare'],
ipad: ['iPad Pro', 'iPad', 'iPad Mini 4', 'iOS 11', 'Accessories', 'Compare'],
iphone: ['iPhone X', 'iPhone 8', 'iPhone 7', 'iPhone 6s', 'iPhone SE', 'iOS 11', 'Accessories', 'Compare'],
watch: ['Apple Watch 3', 'Apple Watch Nike+', 'Apple Watch Hermes', 'Apple Watch Edition', 'Apple Watch 1', 'Watch OS', 'Bands', 'Accessories', 'Compare'],
tv: ['Apple TV 4k', 'Apple TV', 'TV App', 'Accessories', 'Compare'],
music: ['Apple Music', 'iTunes', 'HomePod', 'iPod Touch', 'Music Accessories', 'Gift Cards'],
support: ['Apple doesn\'t support anything older than iOS11']
};
您可以使用 componentWillReceiveProps
解决您的问题,或者,如果您想要面向未来,使用新的静态 getDerivedStateFromProps
.
class TopNavSub extends Component {
constructor(props) {
super(props);
//This could be extracted to avoid repetition, I'm copy-pasting it
const currentProduct = Object.keys(subNavLinks)
.filter(subNavLinksKey => subNavLinksKey === this.props.match.params.product);
this.state = {
navLinks: subNavLinks[currentProduct[0]],
product: this.props.match.params.product,
currentProduct
}
}
//IMPORTANT: USE EITHER ONE OR THE OTHER
//I wrote both options for demonstration purposes
componentWillReceiveProps(nextProps, prevState) {
const currentProduct = Object.keys(subNavLinks)
.filter(subNavLinksKey => subNavLinksKey === nextProps.match.params.product);
this.setState({
product: nextProps.match.params.product,
navLinks: subNavLinks[currentProduct[0]],
currentProduct
});
}
static getDerivedStateFromProps(nextProps, prevState) {
const currentProduct = Object.keys(subNavLinks)
.filter(subNavLinksKey => subNavLinksKey === nextProps.match.params.product);
return {
product: nextProps.match.params.product,
navLinks: subNavLinks[currentProduct[0]],
currentProduct
};
}
render() {
return (
(!this.state.navLinks)
?
null
:
<ul>
{
this.state.navLinks.map(product => {
return (
<li key={product}>{product}</li>
)
})
}
</ul>
)
}
};
更新
关于你的第二个问题,一个好的通用规则是,如果你不需要处理用户输入事件或保存外部数据(比如来自后端的数据 API),你 ("""PROBABLY""") 不需要组件状态。在这种情况下,您是从 props 派生整个状态而不进行任何事件处理,因此您可以将此组件简单地编写为一个函数,如下所示:
const TopNavSub = props => {
//Since you're using ES6 you can change filter for find to get a single element instead of an array
const currentProduct = Object.keys(subNavLinks)
.find(subNavLinksKey => subNavLinksKey === props.match.params.product);
const navLinks = subNavLinks[currentProduct];
return (
(!navLinks)
?
null
:
<ul>
{
navLinks.map(product => (li key={product}>{product}</li>))
}
</ul>
);
};
通过 react router 4
了解动态路由,同时决定将功能组件重构为 class 组件。这背后的原因,如果我错了请纠正我,是 TopNavSub
组件从 react router 4
从 this.props.match.params.<var>
获取信息并选择要呈现的内容。
对我来说,这听起来像是状态的改变。唯一让我怀疑的是状态永远不会在同一条路线上改变并且每条路线总是不同所以我不确定这是否与处理状态相同。例如,/mac
将仅映射 mac
数组中的项目等
我被告知直接在 this.state
中使用 this.props
是一个糟糕的选择,而是使用 componentWillReceiveProps()
但 React 团队似乎对此感觉不佳,UNSAFE_componentWillReceiveProps(),但它什么也没解决。
TLDR;
所以这里的问题是,重构后,我的数据只显示第一个初始路由,之后无论选择哪条路由,除非刷新,否则只会显示初始选择的路由数据(f5
)。
TopNavSub.js
import React, { Component } from 'react';
// data
import { subNavLinks } from './navigationData';
class TopNavSub extends Component {
constructor(props) {
super(props);
this.state = {
navLinks : { ...subNavLinks },
product : this.props.match.params.product,
currentProduct : Object.keys(subNavLinks).filter(subNavLinksKey => subNavLinksKey === this.props.match.params.product),
}
}
render() {
return (
(!this.state.navLinks)
?
null
:
<ul>
{
this.state.navLinks[this.state.product].map(product => {
return (
<li key={ product }>{ product }</li>
)
})
}
</ul>
)
}
};
export default TopNavSub;
navigationData.js
export const subNavLinks = {
mac: ['MacBook', 'MacBook Air', 'MacBook Pro', 'iMac', 'iMac Pro', 'Mac Pro', 'Mac Mini', 'Accessories', 'High Sierra', 'Compare'],
ipad: ['iPad Pro', 'iPad', 'iPad Mini 4', 'iOS 11', 'Accessories', 'Compare'],
iphone: ['iPhone X', 'iPhone 8', 'iPhone 7', 'iPhone 6s', 'iPhone SE', 'iOS 11', 'Accessories', 'Compare'],
watch: ['Apple Watch 3', 'Apple Watch Nike+', 'Apple Watch Hermes', 'Apple Watch Edition', 'Apple Watch 1', 'Watch OS', 'Bands', 'Accessories', 'Compare'],
tv: ['Apple TV 4k', 'Apple TV', 'TV App', 'Accessories', 'Compare'],
music: ['Apple Music', 'iTunes', 'HomePod', 'iPod Touch', 'Music Accessories', 'Gift Cards'],
support: ['Apple doesn\'t support anything older than iOS11']
};
您可以使用 componentWillReceiveProps
解决您的问题,或者,如果您想要面向未来,使用新的静态 getDerivedStateFromProps
.
class TopNavSub extends Component {
constructor(props) {
super(props);
//This could be extracted to avoid repetition, I'm copy-pasting it
const currentProduct = Object.keys(subNavLinks)
.filter(subNavLinksKey => subNavLinksKey === this.props.match.params.product);
this.state = {
navLinks: subNavLinks[currentProduct[0]],
product: this.props.match.params.product,
currentProduct
}
}
//IMPORTANT: USE EITHER ONE OR THE OTHER
//I wrote both options for demonstration purposes
componentWillReceiveProps(nextProps, prevState) {
const currentProduct = Object.keys(subNavLinks)
.filter(subNavLinksKey => subNavLinksKey === nextProps.match.params.product);
this.setState({
product: nextProps.match.params.product,
navLinks: subNavLinks[currentProduct[0]],
currentProduct
});
}
static getDerivedStateFromProps(nextProps, prevState) {
const currentProduct = Object.keys(subNavLinks)
.filter(subNavLinksKey => subNavLinksKey === nextProps.match.params.product);
return {
product: nextProps.match.params.product,
navLinks: subNavLinks[currentProduct[0]],
currentProduct
};
}
render() {
return (
(!this.state.navLinks)
?
null
:
<ul>
{
this.state.navLinks.map(product => {
return (
<li key={product}>{product}</li>
)
})
}
</ul>
)
}
};
更新
关于你的第二个问题,一个好的通用规则是,如果你不需要处理用户输入事件或保存外部数据(比如来自后端的数据 API),你 ("""PROBABLY""") 不需要组件状态。在这种情况下,您是从 props 派生整个状态而不进行任何事件处理,因此您可以将此组件简单地编写为一个函数,如下所示:
const TopNavSub = props => {
//Since you're using ES6 you can change filter for find to get a single element instead of an array
const currentProduct = Object.keys(subNavLinks)
.find(subNavLinksKey => subNavLinksKey === props.match.params.product);
const navLinks = subNavLinks[currentProduct];
return (
(!navLinks)
?
null
:
<ul>
{
navLinks.map(product => (li key={product}>{product}</li>))
}
</ul>
);
};