父传子
使用props传值:子组件通过this.props进行接收就可以了
父组件
import { PureComponent, Fragment } from 'react'
import DocumentTitle from 'react-document-title'
import router from 'umi/router'
import styled from 'styled-components'
import withStyled from '@@/styles/withStyled'
import MineList from '../widgets/MineList' // 子组件
const MineInfo = withStyled(styled.div`
position: relative;
z-index: 10;
margin: -20px 0 0;
padding: 20px 20px 0;
width: 100%;
// backgroundColor是withStyled里面的变量
background-color: ${(props) => props.theme.backgroundColor};
border-radius: 20px 20px 0 0;
`)
const list = [
{
icon: iconPlay,
title: '我的课程',
rightIcon: iconRight,
onClick: () => router.replace({ pathname: '/lesson' })
},
{
icon: iconLogo,
title: '关于小帮',
rightIcon: iconRight,
onClick: () => router.push({ pathname: '/document/about' })
}
]
class MinePage extends PureComponent {
constructor(props) {
super(props)
}
render() {
const { account: { account = {} } } = this.props
return (
<DocumentTitle title='我的'>
<Fragment>
<MineInfo>
{list.map((item, index) => (
// {...item} 向子组件传递参数
// {...item} 向子组件传递参数
// {...item} 向子组件传递参数
<MineList {...item} key={index} /> // 子组件
))}
</MineInfo>
</Fragment>
</DocumentTitle>
)
}
}
export default MinePage
子组件
import PropTypes from 'prop-types'
import styled from 'styled-components'
import withStyled from '@@/styles/withStyled'
const Container = withStyled(styled.div`
width: 100%;
height: 76px;
display: flex;
align-items: center;
img:nth-of-type(1) {
width: 28px;
height: 28px;
margin: 0 16px 0 0;
}
span {
flex: 1;
}
img:nth-of-type(2) {
width: 20px;
height: 20px;
}
`)
const MineList = ({ title, icon, rightIcon, onClick }) => {
return (
<Container onClick={onClick}>
<img src={icon} />
<span>{title}</span>
<img src={rightIcon} />
</Container>
)
}
MineList.propTypes = {
title: PropTypes.string.isRequired,
icon: PropTypes.string.isRequired,
rightIcon: PropTypes.string.isRequired,
onClick: PropTypes.func
}
export default MineList
子传父—不带参数
子组件向父组件传值需要绑定一个事件,然后事件是父组件传递过来的this.props.event来进行值的更替
**
父组件
import { PureComponent } from 'react'
import router from 'umi/router'
import PropTypes from 'prop-types'
import styled, { createGlobalStyle } from 'styled-components'
import withStyled from '@@/styles/withStyled'
import GlobalModalRich from '@@/components/Modal/Rich'
import FeedBack from '../FeedBack' // 子组件
const NavbarPaddingSafe = createGlobalStyle`
#app {
padding-bottom: 49px;
}
`
const NavbarContainer = withStyled(styled.div`
position: fixed;
left: 0;
right: 0;
bottom: 0;
margin: 0 auto;
width: 100%;
height: auto;
max-width: ${(props) => props.theme.maxWidth};
box-shadow: 0 -6px 12px 0 rgba(0, 0, 0, 0.04);
background-color: #ffffff;
`)
const NavbarGroup = withStyled(styled.div`
display: flex;
align-items: center;
padding: 7px 0 6px;
`)
const NavbarButton = withStyled(styled.div`
flex: 25;
padding: 22px 0 0;
font-size: 10px;
line-height: 14px;
color: ${(props) => props.theme.font400};
text-align: center;
background-size: 20px 20px;
background-repeat: no-repeat;
background-position: top center;
background-image: url(${(props) => props.image});
`)
class ResourceNavbarButton extends PureComponent {
constructor(props) {
super(props)
// uniqueId、feedbackStatus是父组件传递进来的值
const { uniqueId, feedbackStatus } = this.props
this.uniqueId = uniqueId
this.state = {
feedbackStatus: feedbackStatus
}
}
componentWillUnmount() {
GlobalModalRich.hide()
}
action = {
handleShowFeedback: () => {
const feedback = {
title: '评价本节',
// 调用子组件
// handleConfirm接收子组件的事件或参数
// handleConfirm接收子组件的事件或参数
// handleConfirm接收子组件的事件或参数
children: <FeedBack uniqueId={this.uniqueId} handleConfirm={() => { this.action.handleFeedback() }} />,
onClick: GlobalModalRich.hide
}
GlobalModalRich.show(feedback)
},
// 接收到子组件到事件或状态后,触发到事件
// 接收到子组件到事件或状态后,触发到事件
// 接收到子组件到事件或状态后,触发到事件
handleFeedback: () => {
this.setState({ feedbackStatus: true })
this.props.handleConfirm()
}
}
render() {
const { feedbackStatus } = this.state
const { noteListStatus } = this.props
return (
<NavbarContainer className='ios-padding-safe'>
<NavbarPaddingSafe />
<NavbarGroup>
<NavbarButton image={iconFeedback} onClick={() => this.action.handleShowFeedback()}>
<span>评价本节</span>
</NavbarButton>
</NavbarGroup>
</NavbarContainer>
)
}
}
ResourceNavbarButton.propTypes = {
uniqueId: PropTypes.string.isRequired,
feedbackStatus: PropTypes.bool.isRequired,
noteListStatus: PropTypes.bool.isRequired
}
export default ResourceNavbarButton
子组件
import { PureComponent } from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import classnames from 'classnames'
import withStyled from '@@/styles/withStyled'
import resourceService from '@@/services/resource'
import GlobalModalRich from '@@/components/Modal/Rich'
import iconBad from './icon-bad.png'
import iconBadChecked from './icon-bad-checked.png'
import iconGood from './icon-good.png'
import iconGoodChecked from './icon-good-checked.png'
import iconSimple from './icon-simple.png'
import iconSimpleChecked from './icon-simple-checked.png'
const Container = withStyled(styled.div`
display: flex;
align-items: center;
justify-content: center;
`)
const Item = withStyled(styled.div`
flex: 1;
font-size: 14PX;
display: flex;
flex-direction: column;
align-items: center;
color: ${(props) => props.theme.font100};
padding: 28px 0 0;
background-size: 24px 24px;
background-repeat: no-repeat;
background-position: top center;
span {
font-size: 10PX;
color: ${(props) => props.theme.font400};
}
&.focus {
span {
color: #FFC300;
}
}
&.bad {
background-image: url(${iconBad});
&.focus {
background-image: url(${iconBadChecked});
}
}
&.good {
background-image: url(${iconGood});
&.focus {
background-image: url(${iconGoodChecked});
}
}
&.simple {
background-image: url(${iconSimple});
&.focus {
background-image: url(${iconSimpleChecked});
}
}
`)
class FeedBack extends PureComponent {
constructor(props) {
super(props)
this.state = {
checkedValue: ''
}
}
action = {
handleFetch: async (type, name) => {
const { checkedValue } = this.state
if (checkedValue) {
return false
}
const { uniqueId } = this.props
this.setState({
checkedValue: name
})
try {
await resourceService.feedback(uniqueId, { feedBackType: type })
} catch (error) {
console.warn(error.message)
} finally {
setTimeout(GlobalModalRich.hide, 500)
// 向父组件发送事件,也可以传递参数
// 向父组件发送事件,也可以传递参数
// 向父组件发送事件,也可以传递参数
this.props.handleConfirm()
}
}
}
render() {
const { checkedValue } = this.state
const badClassName = classnames('bad', { focus: checkedValue === 'bad' })
const goodClassName = classnames('good', { focus: checkedValue === 'good' })
const simpleClassName = classnames('simple', { focus: checkedValue === 'simple' })
return (
<Container>
<Item className={badClassName} onClick={() => this.action.handleFetch(1, 'bad')}>
<span>没听懂</span>
</Item>
<Item className={goodClassName} onClick={() => this.action.handleFetch(2, 'good')}>
<span>有收获</span>
</Item>
<Item className={simpleClassName} onClick={() => this.action.handleFetch(0, 'simple')}>
<span>太简单</span>
</Item>
</Container>
)
}
}
FeedBack.propTypes = {
uniqueId: PropTypes.string.isRequired
}
export default FeedBack
子传父—带参数
父组件
import { PureComponent, Fragment } from 'react'
import DocumentTitle from 'react-document-title'
import ExerciseCard from '../widgets/ExerciseCard'
class DetailPage extends PureComponent {
constructor(props) {
super(props)
}
action = {
handleChangeDate: (exerciseId, type, value) => {
console.log(exerciseId, type, value)// 获取子组件传递过来的值
},
}
render(){
return (
<DocumentTitle title={title}>
<Fragment>
{exerciseList.map(item => (
<ExerciseCard
{...item}
isFinished={isFinished}
handleChangeDate={(exerciseId, type, value) => this.action.handleChangeDate(exerciseId, type, value)}
key={item.rowKey}
/>
))}
</Fragment>
</DocumentTitle>
)
}
}
子组件
import PropTypes from 'prop-types'
import styled from 'styled-components'
import withStyled from '@@/styles/withStyled'
import classnames from 'classnames'
import iconRadio from './icon-radio.png'
import iconRadioCorrect from './icon-radio-correct.png'
import iconCheckout from './icon-checkout.png'
import iconCheckoutCorrect from './icon-checkout-correct.png'
import iconCorrect from './icon-correct.png'
import iconWrong from './icon-wrong.png'
const Container = withStyled(styled.div`
position: relative;
padding: 16px 0 8px;
width: 100%;
`)
const Result = withStyled(styled.img`
position: absolute;
top: 0;
right: 0;
width: 72px;
height: 72px;
`)
const Order = withStyled(styled.div`
height: 20px;
font-size: 16px;
font-weight: bold;
color: ${props => props.theme.font100};
line-height: 20px;
`)
const Title = withStyled(styled.div`
margin: 8px 0 4px;
font-size: 16px;
text-align: justify;
text-align-last: left;
line-height: 24px;
color: ${props => props.theme.font100};
`)
const Answer = withStyled(styled.div`
position: relative;
display: flex;
align-items: center;
margin: 0 0 8px;
padding: 0 0 0 40px;
height: 41px;
background-color: #f5f7fa;
border-radius: 8px;
&:before {
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
display: inline-block;
content: '';
width: 20px;
height: 20px;
background: url(${props => props.icon});
background-repeat: no-repeat;
background-size: cover;
}
&:last-of-type {
margin: 0 0 24px;
}
&.selected {
background-color: rgba(255, 221, 0, 0.1);
}
`)
const Analysis = withStyled(styled.div`
margin: 0 0 40px;
padding: 16px 12px;
background-color: #e5e9f2;
border-radius: 8px;
border: 1px solid rgba(229, 233, 242, 1);
`)
const CorrectAnswer = withStyled(styled.div`
font-size: 14px;
line-height: 18px;
color: ${props => props.theme.font100};
`)
const AnswerAnalysis = withStyled(styled.div`
margin: 4px 0 0;
font-size: 14px;
line-height: 22px;
color: ${props => props.theme.font100};
`)
const judgeResult = status => {
switch (+status) {
case 0:
return ''
case 1:
return iconCorrect
case 2:
return iconWrong
default:
return ''
}
}
const judgeType = type => {
if (type === 0) {
const typeDescription = '单选题'
const iconType = iconRadio
const iconSelect = iconRadioCorrect
return { typeDescription, iconType, iconSelect }
} else if (type === 1) {
const typeDescription = '多选题'
const iconType = iconCheckout
const iconSelect = iconCheckoutCorrect
return { typeDescription, iconType, iconSelect }
}
}
const judgeLabel = index => {
return String.fromCharCode(65 + index)
}
const ExerciseCard = ({ isFinished, exerciseId, title, type, status, content, options, analysis, handleChangeDate }) => {
const iconResult = judgeResult(status)
const { typeDescription, iconType, iconSelect } = judgeType(type)
let rightResult = ''
return (
<Container>
{iconResult && <Result src={iconResult} />}
<Order>
{title}【{typeDescription}】
</Order>
<Title>{content}</Title>
<div>
{options.map((item, index) => {
const { content, isUserAnswer, isCorrect } = item
const label = judgeLabel(index)
if (isCorrect) {
rightResult += label
}
const changeResult = isFinished ? () => { } : () => handleChangeDate(exerciseId, type, index)
const icon = isUserAnswer ? iconSelect : iconType
return (
<Answer className={classnames({ selected: isUserAnswer })} icon={icon} onClick={changeResult} key={index}>
{label}.{content}
</Answer>
)
})}
</div>
{isFinished && (
<Analysis>
<CorrectAnswer>正确答案:{rightResult}</CorrectAnswer>
<AnswerAnalysis>解析:{analysis}</AnswerAnalysis>
</Analysis>
)}
</Container>
)
}
ExerciseCard.propTypes = {
isFinished: PropTypes.bool,
exerciseId: PropTypes.number,
title: PropTypes.string,
type: PropTypes.number,
status: PropTypes.number,
content: PropTypes.string,
options: PropTypes.array,
analysis: PropTypes.string,
handleChangeDate: PropTypes.func,
}
export default ExerciseCard
中间件
withStyled.js—基础颜色组件
import { forwardRef } from 'react'
import { ThemeProvider, keyframes } from 'styled-components'
export const theme = {
// 基础颜色
primaryColor: 'rgba(255, 221, 0, 1)',
secondaryColor: 'rgba(255, 196, 1, 1)',
linkColor: 'rgba(24, 170, 242, 1)',
borderColor: 'rgba(229, 233, 242, 1)',
backgroundColor: 'rgba(245, 247, 250, 1)',
maskColor: 'rgba(0, 0, 0, 0.5)',
shadowColor: 'rgba(200, 200, 200, 0.05)',
highLightColor: 'rgba(34, 34, 34, 1);',
// 基金相关
increaseColor: '#FC4447',
decreaseColor: '#3CC86B',
// 层级
maskZindex: 100,
popupZindex: 200,
dialogZindex: 300,
// 字体颜色
font100: '#324057',
font200: '#475669',
font300: '#5E6D82',
font400: '#8492A6',
font500: '#99A9BF',
font600: '#C0CCDA',
importantfont: '#FF8026',
// 宽度
maxWidth: '480PX',
// 加载器
loaderPrimaryColor: 'rgba(239, 242, 247, 1)',
loaderSecondaryColor: 'rgba(239, 242, 247, 0.6)'
}
export const autofill = keyframes`
from {
background - color: transparent;
}
to {
background - color: transparent;
}
`
export const popupCenter = keyframes`
0% {
transform: translateY(-50%) scale(0.8);
}
100% {
transform: translateY(-50%) scale(1);
}
`
export const riseNavbar = keyframes`
0% {
bottom: -64px;
}
100% {
bottom: 0;
}
`
const withStyled = (Component) => {
const StyledComponent = (props, ref) => (
<ThemeProvider theme={theme}>
<Component ref={ref} {...props} />
</ThemeProvider>
)
return forwardRef(StyledComponent)
}
export default withStyled
GlobalModalRich.js—模态框组件
import PropTypes from 'prop-types'
import styled from 'styled-components'
import withStyled from '@@/styles/withStyled'
import { MountPoint } from '@@/utils/dom'
import iconClose from '@@/assets/icons/icon-close.png'
const MaskBasic = withStyled(styled.div`
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: ${(props) => props.theme.maskColor};
z-index: ${(props) => props.theme.maskZindex};
`)
const MaskCenter = withStyled(styled.div`
position: absolute;
left: 0;
right: 0;
top: 45%;
transform: translateY(-50%);
padding: 20px;
text-align: center;
z-index: ${(props) => props.theme.dialogZindex};
animation: ${popupCenter} 200ms linear forwards;
`)
const ModalContainer = withStyled(styled.div`
display: inline-block;
margin: 0 auto;
padding: 25px 20px;
max-width: 360px;
min-width: 240px;
width: 86%;
text-align: center;
border-radius: 10px;
background-color: #ffffff;
`)
const ModalTitle = withStyled(styled.h4`
font-size: 20px;
font-weight: bold;
line-height: 1.5;
color: ${(props) => props.theme.font100};
text-align: center;
`)
const ModalContent = withStyled(styled.div`
padding: 30px 0 25px;
`)
const ModalClose = styled.div`
margin: 40px auto 0;
width: 26px;
height: 26px;
background-size: 100% 100%;
background-repeat: no-repeat;
background-image: url(${iconClose});
`
const ModalComponent = ({ title, children, onClick }) => (
<MaskBasic onClick={onClick}>
<MaskCenter>
<ModalContainer onClick={(e) => e.stopPropagation()}>
<ModalTitle>{title}</ModalTitle>
<ModalContent>{children}</ModalContent>
</ModalContainer>
<ModalClose />
</MaskCenter>
</MaskBasic>
)
ModalComponent.defaultProps = {
title: '提示',
children: '请输入提示内容',
onClick: () => { }
}
ModalComponent.propTypes = {
title: PropTypes.string.isRequired,
children: PropTypes.node.isRequired,
onClick: PropTypes.func.isRequired
}
class GlobalModalRich extends MountPoint {
hide = () => {
this.removeMountPoint()
}
show = (options) => {
this.render(<ModalComponent onClick={this.hide} {...options} />)
}
}
export default new GlobalModalRich()