React 生命周期方法顺序
React lifecycle methods order
我的项目有无限的幻灯片放映,我不确定我的方法是否顺序正确。我在 componentWillMount()
上调用 fetch 函数,然后在 componentDidMount()
..
上使用该数据
问题可能是其他原因,但以前可以,但现在不行了..
componentWillMount() {
this.props.getAdverts();
}
componentDidMount() {
var index = 0;
setInterval(() => {
this.setState({
bg: this.props.adverts ? this.props.adverts[index++].photo: this.state.bg,
text: this.props.adverts ? this.props.adverts[index++].text: this.state.text
})
if(index === this.props.adverts.length) index = 0;
}, 4000)
}
当我记录 this.props.adverts
时,它是数组..
错误是:Cannot read property 'photo' of undefined
或Cannot read property 'text' of undefined
STACKBLITZ link:https://stackblitz.com/edit/react-sshop
这是您可能希望如何使用当前代码执行此操作的示例。我不同意这样做的方式,但对于初学者来说,这应该可以帮助您开始探索更像 React 的编码方式。
// This is a composable function, we will pass it in to setInterval.
// we need access to the component, so we will pass it in and then
// return the function signature that setInterval wants to call
const changeAdvertComposer = function changeAdvertComposer(component) {
// we start at -1 so the first call asks for 0
let index = -1;
// return the function that setInterval will call
return function changeAdvert() {
const adverts = component.props.adverts;
if (!adverts || !adverts.length) {
// we have no advertisements so lets exit
return;
}
index++;
// reset our index if needed
if (index >= adverts.length) {
index = 0;
}
// increment and grab our object
const advert = adverts[index];
// grab our state or a default failover structure
const state = component.state.advert || { bg: '', text: '' };
// set our state
component.setState({
advert: {
bg: advert.photo || state.bg,
text: advert.text || state.text,
}
});
}
};
export ExampleAdvertManager extends Component {
// setup some validations on our props
static propTypes = {
getAdverts: PropTypes.func.isRequired,
adverts: PropTypes.arrayOf(PropTypes.shape({
photo: PropTypes.string,
text: PropTypes.string
}))
}
constructor(props) {
super(props);
// we will store the state in the interval object so we can
// check for null (simple way to find out if loading)
this.state = {
advert: null
};
// we will store the ref to our interval here
this._intervalRef = null;
}
componentDidMount() {
// we are loaded let's call our data action loader
this.props.getAdverts();
}
componentWillUpdate() {
// do we have any ads?
const adlength = this.props.adverts ? this.props.adverts.length : 0;
if (adlength && !this._intervalRef) {
// we have ads and we have not setup the interval so lets do it
this._intervalRef = setInterval(changeAdvertComposer(this), 4000);
} else if (!adlength && this._intervalRef) {
// we have no ads but we have an interval so lets stop it
clearInterval(this._intervalRef);
this._intervalRef = null;
}
}
componentWillUnmount() {
// we are unloading, lets clear up the interval so we don't leak
if (this._intervalRef) {
clearInterval(this._intervalRef);
this._intervalRef = null;
}
}
render() {
if (this.stage.advert) {
// render here
}
// we don't have data yet
return null; // or some loading view
}
}
我在这个例子中可能做得太过火了,我这样做的时间太长了,而且真的依赖于单元测试的可组合性。这让我很难不以这种方式思考。虽然我没有让 setState 可组合......兔子洞变得更深了。
真的,我会让间隔计时器成为它自己的一个组件,它呈现 null 并触发对我的组件的回调。只是让一切更干净。那看起来像这样:
class TimerComponent extends PureComponent {
static propTypes = {
onInterval: PropTypes.func.isRequired,
interval: PropTypes.number.isRequired,
immediate: PropTypes.bool,
}
static defaultProps = {
immediate: true,
}
componentDidMount() {
this._intervalRef = setInterval(this.props.onInterval, this.props.interval);
if (this.props.immediate) {
this.props.onInterval();
}
}
componentWillUnmount() {
clearInterval(this._intervalRef);
}
render() {
return null;
}
}
// We will still use the composable function, but in a differnt way.
// The function stays the same
const changeAdvertComposer = function changeAdvertComposer(component) {
// we start at -1 so the first call asks for 0
let index = -1;
// return the function that setInterval will call
return function changeAdvert() {
const adverts = component.props.adverts;
if (!adverts || !adverts.length) {
// we have no advertisements so lets exit
return;
}
index++;
// reset our index if needed
if (index >= adverts.length) {
index = 0;
}
// increment and grab our object
const advert = adverts[index];
// grab our state or a default failover structure
const state = component.state.advert || { bg: '', text: '' };
// set our state
component.setState({
advert: {
bg: advert.photo || state.bg,
text: advert.text || state.text,
}
});
}
};
export ExampleAdvertManager extends Component {
// setup some validations on our props
static propTypes = {
getAdverts: PropTypes.func.isRequired,
adverts: PropTypes.arrayOf(PropTypes.shape({
photo: PropTypes.string,
text: PropTypes.string
}))
}
constructor(props) {
super(props);
// we will store the state in the interval object so we can
// check for null (simple way to find out if loading)
this.state = {
advert: null
};
}
componentDidMount() {
// we are loaded let's call our data action loader
this.props.getAdverts();
}
render() {
if (this.stage.advert) {
return (
<div>
<TimerComponent interval={4000} onInterval={changeAdvertComposer(this)} />
{
// render what you want to do from state
}
</div>
);
} else if (this.props.adverts) {
return (
<TimerComponent key="interval" interval={4000} onInterval={changeAdvertComposer(this)} />
);
}
// we don't have data yet
return null; // or some loading view
}
}
希望对您有所帮助。
我的项目有无限的幻灯片放映,我不确定我的方法是否顺序正确。我在 componentWillMount()
上调用 fetch 函数,然后在 componentDidMount()
..
问题可能是其他原因,但以前可以,但现在不行了..
componentWillMount() {
this.props.getAdverts();
}
componentDidMount() {
var index = 0;
setInterval(() => {
this.setState({
bg: this.props.adverts ? this.props.adverts[index++].photo: this.state.bg,
text: this.props.adverts ? this.props.adverts[index++].text: this.state.text
})
if(index === this.props.adverts.length) index = 0;
}, 4000)
}
当我记录 this.props.adverts
时,它是数组..
错误是:Cannot read property 'photo' of undefined
或Cannot read property 'text' of undefined
STACKBLITZ link:https://stackblitz.com/edit/react-sshop
这是您可能希望如何使用当前代码执行此操作的示例。我不同意这样做的方式,但对于初学者来说,这应该可以帮助您开始探索更像 React 的编码方式。
// This is a composable function, we will pass it in to setInterval.
// we need access to the component, so we will pass it in and then
// return the function signature that setInterval wants to call
const changeAdvertComposer = function changeAdvertComposer(component) {
// we start at -1 so the first call asks for 0
let index = -1;
// return the function that setInterval will call
return function changeAdvert() {
const adverts = component.props.adverts;
if (!adverts || !adverts.length) {
// we have no advertisements so lets exit
return;
}
index++;
// reset our index if needed
if (index >= adverts.length) {
index = 0;
}
// increment and grab our object
const advert = adverts[index];
// grab our state or a default failover structure
const state = component.state.advert || { bg: '', text: '' };
// set our state
component.setState({
advert: {
bg: advert.photo || state.bg,
text: advert.text || state.text,
}
});
}
};
export ExampleAdvertManager extends Component {
// setup some validations on our props
static propTypes = {
getAdverts: PropTypes.func.isRequired,
adverts: PropTypes.arrayOf(PropTypes.shape({
photo: PropTypes.string,
text: PropTypes.string
}))
}
constructor(props) {
super(props);
// we will store the state in the interval object so we can
// check for null (simple way to find out if loading)
this.state = {
advert: null
};
// we will store the ref to our interval here
this._intervalRef = null;
}
componentDidMount() {
// we are loaded let's call our data action loader
this.props.getAdverts();
}
componentWillUpdate() {
// do we have any ads?
const adlength = this.props.adverts ? this.props.adverts.length : 0;
if (adlength && !this._intervalRef) {
// we have ads and we have not setup the interval so lets do it
this._intervalRef = setInterval(changeAdvertComposer(this), 4000);
} else if (!adlength && this._intervalRef) {
// we have no ads but we have an interval so lets stop it
clearInterval(this._intervalRef);
this._intervalRef = null;
}
}
componentWillUnmount() {
// we are unloading, lets clear up the interval so we don't leak
if (this._intervalRef) {
clearInterval(this._intervalRef);
this._intervalRef = null;
}
}
render() {
if (this.stage.advert) {
// render here
}
// we don't have data yet
return null; // or some loading view
}
}
我在这个例子中可能做得太过火了,我这样做的时间太长了,而且真的依赖于单元测试的可组合性。这让我很难不以这种方式思考。虽然我没有让 setState 可组合......兔子洞变得更深了。
真的,我会让间隔计时器成为它自己的一个组件,它呈现 null 并触发对我的组件的回调。只是让一切更干净。那看起来像这样:
class TimerComponent extends PureComponent {
static propTypes = {
onInterval: PropTypes.func.isRequired,
interval: PropTypes.number.isRequired,
immediate: PropTypes.bool,
}
static defaultProps = {
immediate: true,
}
componentDidMount() {
this._intervalRef = setInterval(this.props.onInterval, this.props.interval);
if (this.props.immediate) {
this.props.onInterval();
}
}
componentWillUnmount() {
clearInterval(this._intervalRef);
}
render() {
return null;
}
}
// We will still use the composable function, but in a differnt way.
// The function stays the same
const changeAdvertComposer = function changeAdvertComposer(component) {
// we start at -1 so the first call asks for 0
let index = -1;
// return the function that setInterval will call
return function changeAdvert() {
const adverts = component.props.adverts;
if (!adverts || !adverts.length) {
// we have no advertisements so lets exit
return;
}
index++;
// reset our index if needed
if (index >= adverts.length) {
index = 0;
}
// increment and grab our object
const advert = adverts[index];
// grab our state or a default failover structure
const state = component.state.advert || { bg: '', text: '' };
// set our state
component.setState({
advert: {
bg: advert.photo || state.bg,
text: advert.text || state.text,
}
});
}
};
export ExampleAdvertManager extends Component {
// setup some validations on our props
static propTypes = {
getAdverts: PropTypes.func.isRequired,
adverts: PropTypes.arrayOf(PropTypes.shape({
photo: PropTypes.string,
text: PropTypes.string
}))
}
constructor(props) {
super(props);
// we will store the state in the interval object so we can
// check for null (simple way to find out if loading)
this.state = {
advert: null
};
}
componentDidMount() {
// we are loaded let's call our data action loader
this.props.getAdverts();
}
render() {
if (this.stage.advert) {
return (
<div>
<TimerComponent interval={4000} onInterval={changeAdvertComposer(this)} />
{
// render what you want to do from state
}
</div>
);
} else if (this.props.adverts) {
return (
<TimerComponent key="interval" interval={4000} onInterval={changeAdvertComposer(this)} />
);
}
// we don't have data yet
return null; // or some loading view
}
}
希望对您有所帮助。