我的项目有无限幻灯片,我不确定我的方法是否正确。我在componentWillMount()
上调用了抓取功能,然后在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链接:https://stackblitz.com/edit/react-sshop
答案 0 :(得分:1)
这是您可能希望使用当前代码执行此操作的示例。我不同意这种做法,但对于初学者来说,这应该可以帮助你开始探索更多的React-ish编码方式。
// 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
}
}
我希望这会有所帮助。