在 React Component 的 setState 中使用 Math.random 时组件渲染不正确
Incorrect rendering of a component when using Math.random in setState in React Component
目的是显示对象列表中的项目,但在每次页面刷新时,应从列表中随机选择该项目。在这里,Testimonials
是列表,我想显示此列表中的任何随机项目。如果我使用常量,它工作正常。当我使用随机函数时,它不会显示正确的图像及其关联的项目消息。
我使用 React 16,Next.js,样式组件作为技术。
问题出在员工部分的渲染中。控制台将警告显示为
warning.js?6327:33 Warning: Prop
srcdid not match. Server: "/static/images/testimonials/2.jpg" Client: "/static/images/testimonials/5.png"
这是我的代码片段
import {Component} from 'react';
import Row from '../../../common-util/row';
import Col from '../../../common-util/col';
import {Container, Content, Image, StyledCol, Statement, Title, Designation, Heading, Arrow} from './styles';
const Testimonials = [{
name: 'ACX',
role: 'XYZ',
image: '/static/images/testimonials/1.jpeg',
message: 'KL DSAD E'
}, {
name: 'HJK',
role: 'Growth Hacker',
image: '/static/images/testimonials/2.jpg',
message: 'JKLASD ASDA'
}, {
name: 'ZXCV',
role: 'Product Manager',
image: '/static/images/testimonials/3.jpg',
message: 'KKK'
}, {
name: 'UIP',
role: 'Data Integrity',
image: '/static/images/testimonials/4.JPG',
message: 'LOPO'
}, {
name: 'NMa',
role: 'Sales Evangelist',
image: '/static/images/testimonials/5.png',
message: 'KK D D D'
}];
export default class Employees extends Component {
constructor(props) {
super(props);
this.state = {
currentIndex: parseInt((Math.random()*10))%5,
};
}
render() {
let {currentIndex} = this.state;
return (
<Container>
<Content>
<Title>Our employees say...</Title>
</Content>
<Row>
<Col xs={1} sm={1} md={2} lg={2}>
<Arrow className={currentIndex === 0 ? 'disabled' : ''} onClick={this.handleClick.bind(this, 'DECREMENT')}>{'<'}</Arrow>
</Col>
<Col xs={10} sm={10} md={8} lg={8}>{this.currentItem(currentIndex)}</Col>
<Col xs={1} sm={1} md={2} lg={2}>
<Arrow className={currentIndex === Testimonials.length - 1 ? 'disabled' : ''} onClick={this.handleClick.bind(this, 'INCREMENT')}>{'>'}</Arrow>
</Col>
</Row>
</Container>
);
}
currentItem(currentIndex) {
const item = Testimonials[currentIndex];
return (
<Row>
<StyledCol xs={4} md={4} lg={3}>
<Image src={item.image} alt={item.name} />
</StyledCol>
<Col xs={8} md={8} lg={9}>
<Statement>{item.message}</Statement>
<Heading className='font-Bold'>{item.name},</Heading>
<Designation className='font-DemiBold'>{item.role}</Designation>
</Col>
</Row>
);
}
handleClick(type: string) {
let {currentIndex} = this.state;
switch (type) {
case 'DECREMENT':
this.setState({
currentIndex: currentIndex - 1
});
break;
case 'INCREMENT':
this.setState({
currentIndex: currentIndex + 1
});
break;
default:
}
}
}
对应的样式为
import styled from 'styled-components';
import Col from '../../../common-util/col';
import Grid from '../../../common-util/grid';
import H4 from '../../../common-util/headers/h4';
import H5 from '../../../common-util/headers/h5';
export const Container = styled(Grid)`
padding-bottom: 20px;
`;
export const Content = styled.div`
display: flex;
flex-flow: row wrap;
width: 100%;
justify-content: center;
margin-bottom: 20px;
`;
export const Title = styled.h1`
font-size: 42px;
line-height: 1.0;
letter-spacing: -0.3px;
text-align: justify;
font-weight: 500;
`;
export const Image = styled.img`
width: 100%;
`;
export const Statement = styled.p`
padding: 15px 20px;
background: url(/static/images/svg/top-left-bg.svg) top left no-repeat, url(/static/images/svg/bottom-right-bg.svg) bottom right no-repeat;
background-size: 20px;
line-height: 2.3;
letter-spacing: -0.2px;
font-size: 16px;
margin: 0
`;
export const Heading = H4.extend`
color: #4990e2;
text-align: left;
font-weight: bold;
line-height: 1.2;
margin-bottom: 5px;
margin-left: 20px;
`;
export const Designation = H5.extend`
text-align: left;
font-weight: 600;
line-height: 1.22;
letter-spacing: -0.2px;
margin-top: 0;
margin-left: 20px;
`;
export const Arrow = styled.div`
margin: auto;
color: grey;
font-size: 20px;
font-weight:lighter;
cursor: pointer;
&:hover {
font-size: 22px;
}
&.disabled {
pointer-events: none;
&:hover {
font-size: 20px;
}
}
`;
export const StyledCol = Col.extend`
margin: auto;
`;
就是这个问题。
this.state = {
currentIndex: parseInt((Math.random()*10))%5,
};
这将在服务器和浏览器中调用,导致呈现的标记不匹配。
您可以通过确保只在浏览器中调用 random 来解决此问题:
this.state = {
currentIndex: 0,
};
componentDidMount(){
this.setState({ currentIndex: parseInt((Math.random()*10))%5 })
}
使用 React 16.8.0 中引入的 useState and useEffect 钩子:
const [selectedImage, setImage] = useState(<defaultImage>);
useEffect(() => {
setImage(randomImage());
}, [selectedImage]);
目的是显示对象列表中的项目,但在每次页面刷新时,应从列表中随机选择该项目。在这里,Testimonials
是列表,我想显示此列表中的任何随机项目。如果我使用常量,它工作正常。当我使用随机函数时,它不会显示正确的图像及其关联的项目消息。
我使用 React 16,Next.js,样式组件作为技术。
问题出在员工部分的渲染中。控制台将警告显示为
warning.js?6327:33 Warning: Prop
srcdid not match. Server: "/static/images/testimonials/2.jpg" Client: "/static/images/testimonials/5.png"
这是我的代码片段
import {Component} from 'react';
import Row from '../../../common-util/row';
import Col from '../../../common-util/col';
import {Container, Content, Image, StyledCol, Statement, Title, Designation, Heading, Arrow} from './styles';
const Testimonials = [{
name: 'ACX',
role: 'XYZ',
image: '/static/images/testimonials/1.jpeg',
message: 'KL DSAD E'
}, {
name: 'HJK',
role: 'Growth Hacker',
image: '/static/images/testimonials/2.jpg',
message: 'JKLASD ASDA'
}, {
name: 'ZXCV',
role: 'Product Manager',
image: '/static/images/testimonials/3.jpg',
message: 'KKK'
}, {
name: 'UIP',
role: 'Data Integrity',
image: '/static/images/testimonials/4.JPG',
message: 'LOPO'
}, {
name: 'NMa',
role: 'Sales Evangelist',
image: '/static/images/testimonials/5.png',
message: 'KK D D D'
}];
export default class Employees extends Component {
constructor(props) {
super(props);
this.state = {
currentIndex: parseInt((Math.random()*10))%5,
};
}
render() {
let {currentIndex} = this.state;
return (
<Container>
<Content>
<Title>Our employees say...</Title>
</Content>
<Row>
<Col xs={1} sm={1} md={2} lg={2}>
<Arrow className={currentIndex === 0 ? 'disabled' : ''} onClick={this.handleClick.bind(this, 'DECREMENT')}>{'<'}</Arrow>
</Col>
<Col xs={10} sm={10} md={8} lg={8}>{this.currentItem(currentIndex)}</Col>
<Col xs={1} sm={1} md={2} lg={2}>
<Arrow className={currentIndex === Testimonials.length - 1 ? 'disabled' : ''} onClick={this.handleClick.bind(this, 'INCREMENT')}>{'>'}</Arrow>
</Col>
</Row>
</Container>
);
}
currentItem(currentIndex) {
const item = Testimonials[currentIndex];
return (
<Row>
<StyledCol xs={4} md={4} lg={3}>
<Image src={item.image} alt={item.name} />
</StyledCol>
<Col xs={8} md={8} lg={9}>
<Statement>{item.message}</Statement>
<Heading className='font-Bold'>{item.name},</Heading>
<Designation className='font-DemiBold'>{item.role}</Designation>
</Col>
</Row>
);
}
handleClick(type: string) {
let {currentIndex} = this.state;
switch (type) {
case 'DECREMENT':
this.setState({
currentIndex: currentIndex - 1
});
break;
case 'INCREMENT':
this.setState({
currentIndex: currentIndex + 1
});
break;
default:
}
}
}
对应的样式为
import styled from 'styled-components';
import Col from '../../../common-util/col';
import Grid from '../../../common-util/grid';
import H4 from '../../../common-util/headers/h4';
import H5 from '../../../common-util/headers/h5';
export const Container = styled(Grid)`
padding-bottom: 20px;
`;
export const Content = styled.div`
display: flex;
flex-flow: row wrap;
width: 100%;
justify-content: center;
margin-bottom: 20px;
`;
export const Title = styled.h1`
font-size: 42px;
line-height: 1.0;
letter-spacing: -0.3px;
text-align: justify;
font-weight: 500;
`;
export const Image = styled.img`
width: 100%;
`;
export const Statement = styled.p`
padding: 15px 20px;
background: url(/static/images/svg/top-left-bg.svg) top left no-repeat, url(/static/images/svg/bottom-right-bg.svg) bottom right no-repeat;
background-size: 20px;
line-height: 2.3;
letter-spacing: -0.2px;
font-size: 16px;
margin: 0
`;
export const Heading = H4.extend`
color: #4990e2;
text-align: left;
font-weight: bold;
line-height: 1.2;
margin-bottom: 5px;
margin-left: 20px;
`;
export const Designation = H5.extend`
text-align: left;
font-weight: 600;
line-height: 1.22;
letter-spacing: -0.2px;
margin-top: 0;
margin-left: 20px;
`;
export const Arrow = styled.div`
margin: auto;
color: grey;
font-size: 20px;
font-weight:lighter;
cursor: pointer;
&:hover {
font-size: 22px;
}
&.disabled {
pointer-events: none;
&:hover {
font-size: 20px;
}
}
`;
export const StyledCol = Col.extend`
margin: auto;
`;
就是这个问题。
this.state = {
currentIndex: parseInt((Math.random()*10))%5,
};
这将在服务器和浏览器中调用,导致呈现的标记不匹配。
您可以通过确保只在浏览器中调用 random 来解决此问题:
this.state = {
currentIndex: 0,
};
componentDidMount(){
this.setState({ currentIndex: parseInt((Math.random()*10))%5 })
}
使用 React 16.8.0 中引入的 useState and useEffect 钩子:
const [selectedImage, setImage] = useState(<defaultImage>);
useEffect(() => {
setImage(randomImage());
}, [selectedImage]);