父传子

使用props传值:子组件通过this.props进行接收就可以了

父组件

  1. import { PureComponent, Fragment } from 'react'
  2. import DocumentTitle from 'react-document-title'
  3. import router from 'umi/router'
  4. import styled from 'styled-components'
  5. import withStyled from '@@/styles/withStyled'
  6. import MineList from '../widgets/MineList' // 子组件
  7. const MineInfo = withStyled(styled.div`
  8. position: relative;
  9. z-index: 10;
  10. margin: -20px 0 0;
  11. padding: 20px 20px 0;
  12. width: 100%;
  13. // backgroundColor是withStyled里面的变量
  14. background-color: ${(props) => props.theme.backgroundColor};
  15. border-radius: 20px 20px 0 0;
  16. `)
  17. const list = [
  18. {
  19. icon: iconPlay,
  20. title: '我的课程',
  21. rightIcon: iconRight,
  22. onClick: () => router.replace({ pathname: '/lesson' })
  23. },
  24. {
  25. icon: iconLogo,
  26. title: '关于小帮',
  27. rightIcon: iconRight,
  28. onClick: () => router.push({ pathname: '/document/about' })
  29. }
  30. ]
  31. class MinePage extends PureComponent {
  32. constructor(props) {
  33. super(props)
  34. }
  35. render() {
  36. const { account: { account = {} } } = this.props
  37. return (
  38. <DocumentTitle title='我的'>
  39. <Fragment>
  40. <MineInfo>
  41. {list.map((item, index) => (
  42. // {...item} 向子组件传递参数
  43. // {...item} 向子组件传递参数
  44. // {...item} 向子组件传递参数
  45. <MineList {...item} key={index} /> // 子组件
  46. ))}
  47. </MineInfo>
  48. </Fragment>
  49. </DocumentTitle>
  50. )
  51. }
  52. }
  53. export default MinePage

子组件

  1. import PropTypes from 'prop-types'
  2. import styled from 'styled-components'
  3. import withStyled from '@@/styles/withStyled'
  4. const Container = withStyled(styled.div`
  5. width: 100%;
  6. height: 76px;
  7. display: flex;
  8. align-items: center;
  9. img:nth-of-type(1) {
  10. width: 28px;
  11. height: 28px;
  12. margin: 0 16px 0 0;
  13. }
  14. span {
  15. flex: 1;
  16. }
  17. img:nth-of-type(2) {
  18. width: 20px;
  19. height: 20px;
  20. }
  21. `)
  22. const MineList = ({ title, icon, rightIcon, onClick }) => {
  23. return (
  24. <Container onClick={onClick}>
  25. <img src={icon} />
  26. <span>{title}</span>
  27. <img src={rightIcon} />
  28. </Container>
  29. )
  30. }
  31. MineList.propTypes = {
  32. title: PropTypes.string.isRequired,
  33. icon: PropTypes.string.isRequired,
  34. rightIcon: PropTypes.string.isRequired,
  35. onClick: PropTypes.func
  36. }
  37. export default MineList

子传父—不带参数

子组件向父组件传值需要绑定一个事件,然后事件是父组件传递过来的this.props.event来进行值的更替
**
父组件

  1. import { PureComponent } from 'react'
  2. import router from 'umi/router'
  3. import PropTypes from 'prop-types'
  4. import styled, { createGlobalStyle } from 'styled-components'
  5. import withStyled from '@@/styles/withStyled'
  6. import GlobalModalRich from '@@/components/Modal/Rich'
  7. import FeedBack from '../FeedBack' // 子组件
  8. const NavbarPaddingSafe = createGlobalStyle`
  9. #app {
  10. padding-bottom: 49px;
  11. }
  12. `
  13. const NavbarContainer = withStyled(styled.div`
  14. position: fixed;
  15. left: 0;
  16. right: 0;
  17. bottom: 0;
  18. margin: 0 auto;
  19. width: 100%;
  20. height: auto;
  21. max-width: ${(props) => props.theme.maxWidth};
  22. box-shadow: 0 -6px 12px 0 rgba(0, 0, 0, 0.04);
  23. background-color: #ffffff;
  24. `)
  25. const NavbarGroup = withStyled(styled.div`
  26. display: flex;
  27. align-items: center;
  28. padding: 7px 0 6px;
  29. `)
  30. const NavbarButton = withStyled(styled.div`
  31. flex: 25;
  32. padding: 22px 0 0;
  33. font-size: 10px;
  34. line-height: 14px;
  35. color: ${(props) => props.theme.font400};
  36. text-align: center;
  37. background-size: 20px 20px;
  38. background-repeat: no-repeat;
  39. background-position: top center;
  40. background-image: url(${(props) => props.image});
  41. `)
  42. class ResourceNavbarButton extends PureComponent {
  43. constructor(props) {
  44. super(props)
  45. // uniqueId、feedbackStatus是父组件传递进来的值
  46. const { uniqueId, feedbackStatus } = this.props
  47. this.uniqueId = uniqueId
  48. this.state = {
  49. feedbackStatus: feedbackStatus
  50. }
  51. }
  52. componentWillUnmount() {
  53. GlobalModalRich.hide()
  54. }
  55. action = {
  56. handleShowFeedback: () => {
  57. const feedback = {
  58. title: '评价本节',
  59. // 调用子组件
  60. // handleConfirm接收子组件的事件或参数
  61. // handleConfirm接收子组件的事件或参数
  62. // handleConfirm接收子组件的事件或参数
  63. children: <FeedBack uniqueId={this.uniqueId} handleConfirm={() => { this.action.handleFeedback() }} />,
  64. onClick: GlobalModalRich.hide
  65. }
  66. GlobalModalRich.show(feedback)
  67. },
  68. // 接收到子组件到事件或状态后,触发到事件
  69. // 接收到子组件到事件或状态后,触发到事件
  70. // 接收到子组件到事件或状态后,触发到事件
  71. handleFeedback: () => {
  72. this.setState({ feedbackStatus: true })
  73. this.props.handleConfirm()
  74. }
  75. }
  76. render() {
  77. const { feedbackStatus } = this.state
  78. const { noteListStatus } = this.props
  79. return (
  80. <NavbarContainer className='ios-padding-safe'>
  81. <NavbarPaddingSafe />
  82. <NavbarGroup>
  83. <NavbarButton image={iconFeedback} onClick={() => this.action.handleShowFeedback()}>
  84. <span>评价本节</span>
  85. </NavbarButton>
  86. </NavbarGroup>
  87. </NavbarContainer>
  88. )
  89. }
  90. }
  91. ResourceNavbarButton.propTypes = {
  92. uniqueId: PropTypes.string.isRequired,
  93. feedbackStatus: PropTypes.bool.isRequired,
  94. noteListStatus: PropTypes.bool.isRequired
  95. }
  96. export default ResourceNavbarButton

子组件

  1. import { PureComponent } from 'react'
  2. import PropTypes from 'prop-types'
  3. import styled from 'styled-components'
  4. import classnames from 'classnames'
  5. import withStyled from '@@/styles/withStyled'
  6. import resourceService from '@@/services/resource'
  7. import GlobalModalRich from '@@/components/Modal/Rich'
  8. import iconBad from './icon-bad.png'
  9. import iconBadChecked from './icon-bad-checked.png'
  10. import iconGood from './icon-good.png'
  11. import iconGoodChecked from './icon-good-checked.png'
  12. import iconSimple from './icon-simple.png'
  13. import iconSimpleChecked from './icon-simple-checked.png'
  14. const Container = withStyled(styled.div`
  15. display: flex;
  16. align-items: center;
  17. justify-content: center;
  18. `)
  19. const Item = withStyled(styled.div`
  20. flex: 1;
  21. font-size: 14PX;
  22. display: flex;
  23. flex-direction: column;
  24. align-items: center;
  25. color: ${(props) => props.theme.font100};
  26. padding: 28px 0 0;
  27. background-size: 24px 24px;
  28. background-repeat: no-repeat;
  29. background-position: top center;
  30. span {
  31. font-size: 10PX;
  32. color: ${(props) => props.theme.font400};
  33. }
  34. &.focus {
  35. span {
  36. color: #FFC300;
  37. }
  38. }
  39. &.bad {
  40. background-image: url(${iconBad});
  41. &.focus {
  42. background-image: url(${iconBadChecked});
  43. }
  44. }
  45. &.good {
  46. background-image: url(${iconGood});
  47. &.focus {
  48. background-image: url(${iconGoodChecked});
  49. }
  50. }
  51. &.simple {
  52. background-image: url(${iconSimple});
  53. &.focus {
  54. background-image: url(${iconSimpleChecked});
  55. }
  56. }
  57. `)
  58. class FeedBack extends PureComponent {
  59. constructor(props) {
  60. super(props)
  61. this.state = {
  62. checkedValue: ''
  63. }
  64. }
  65. action = {
  66. handleFetch: async (type, name) => {
  67. const { checkedValue } = this.state
  68. if (checkedValue) {
  69. return false
  70. }
  71. const { uniqueId } = this.props
  72. this.setState({
  73. checkedValue: name
  74. })
  75. try {
  76. await resourceService.feedback(uniqueId, { feedBackType: type })
  77. } catch (error) {
  78. console.warn(error.message)
  79. } finally {
  80. setTimeout(GlobalModalRich.hide, 500)
  81. // 向父组件发送事件,也可以传递参数
  82. // 向父组件发送事件,也可以传递参数
  83. // 向父组件发送事件,也可以传递参数
  84. this.props.handleConfirm()
  85. }
  86. }
  87. }
  88. render() {
  89. const { checkedValue } = this.state
  90. const badClassName = classnames('bad', { focus: checkedValue === 'bad' })
  91. const goodClassName = classnames('good', { focus: checkedValue === 'good' })
  92. const simpleClassName = classnames('simple', { focus: checkedValue === 'simple' })
  93. return (
  94. <Container>
  95. <Item className={badClassName} onClick={() => this.action.handleFetch(1, 'bad')}>
  96. <span>没听懂</span>
  97. </Item>
  98. <Item className={goodClassName} onClick={() => this.action.handleFetch(2, 'good')}>
  99. <span>有收获</span>
  100. </Item>
  101. <Item className={simpleClassName} onClick={() => this.action.handleFetch(0, 'simple')}>
  102. <span>太简单</span>
  103. </Item>
  104. </Container>
  105. )
  106. }
  107. }
  108. FeedBack.propTypes = {
  109. uniqueId: PropTypes.string.isRequired
  110. }
  111. export default FeedBack

子传父—带参数

父组件

  1. import { PureComponent, Fragment } from 'react'
  2. import DocumentTitle from 'react-document-title'
  3. import ExerciseCard from '../widgets/ExerciseCard'
  4. class DetailPage extends PureComponent {
  5. constructor(props) {
  6. super(props)
  7. }
  8. action = {
  9. handleChangeDate: (exerciseId, type, value) => {
  10. console.log(exerciseId, type, value)// 获取子组件传递过来的值
  11. },
  12. }
  13. render(){
  14. return (
  15. <DocumentTitle title={title}>
  16. <Fragment>
  17. {exerciseList.map(item => (
  18. <ExerciseCard
  19. {...item}
  20. isFinished={isFinished}
  21. handleChangeDate={(exerciseId, type, value) => this.action.handleChangeDate(exerciseId, type, value)}
  22. key={item.rowKey}
  23. />
  24. ))}
  25. </Fragment>
  26. </DocumentTitle>
  27. )
  28. }
  29. }

子组件

  1. import PropTypes from 'prop-types'
  2. import styled from 'styled-components'
  3. import withStyled from '@@/styles/withStyled'
  4. import classnames from 'classnames'
  5. import iconRadio from './icon-radio.png'
  6. import iconRadioCorrect from './icon-radio-correct.png'
  7. import iconCheckout from './icon-checkout.png'
  8. import iconCheckoutCorrect from './icon-checkout-correct.png'
  9. import iconCorrect from './icon-correct.png'
  10. import iconWrong from './icon-wrong.png'
  11. const Container = withStyled(styled.div`
  12. position: relative;
  13. padding: 16px 0 8px;
  14. width: 100%;
  15. `)
  16. const Result = withStyled(styled.img`
  17. position: absolute;
  18. top: 0;
  19. right: 0;
  20. width: 72px;
  21. height: 72px;
  22. `)
  23. const Order = withStyled(styled.div`
  24. height: 20px;
  25. font-size: 16px;
  26. font-weight: bold;
  27. color: ${props => props.theme.font100};
  28. line-height: 20px;
  29. `)
  30. const Title = withStyled(styled.div`
  31. margin: 8px 0 4px;
  32. font-size: 16px;
  33. text-align: justify;
  34. text-align-last: left;
  35. line-height: 24px;
  36. color: ${props => props.theme.font100};
  37. `)
  38. const Answer = withStyled(styled.div`
  39. position: relative;
  40. display: flex;
  41. align-items: center;
  42. margin: 0 0 8px;
  43. padding: 0 0 0 40px;
  44. height: 41px;
  45. background-color: #f5f7fa;
  46. border-radius: 8px;
  47. &:before {
  48. position: absolute;
  49. left: 12px;
  50. top: 50%;
  51. transform: translateY(-50%);
  52. display: inline-block;
  53. content: '';
  54. width: 20px;
  55. height: 20px;
  56. background: url(${props => props.icon});
  57. background-repeat: no-repeat;
  58. background-size: cover;
  59. }
  60. &:last-of-type {
  61. margin: 0 0 24px;
  62. }
  63. &.selected {
  64. background-color: rgba(255, 221, 0, 0.1);
  65. }
  66. `)
  67. const Analysis = withStyled(styled.div`
  68. margin: 0 0 40px;
  69. padding: 16px 12px;
  70. background-color: #e5e9f2;
  71. border-radius: 8px;
  72. border: 1px solid rgba(229, 233, 242, 1);
  73. `)
  74. const CorrectAnswer = withStyled(styled.div`
  75. font-size: 14px;
  76. line-height: 18px;
  77. color: ${props => props.theme.font100};
  78. `)
  79. const AnswerAnalysis = withStyled(styled.div`
  80. margin: 4px 0 0;
  81. font-size: 14px;
  82. line-height: 22px;
  83. color: ${props => props.theme.font100};
  84. `)
  85. const judgeResult = status => {
  86. switch (+status) {
  87. case 0:
  88. return ''
  89. case 1:
  90. return iconCorrect
  91. case 2:
  92. return iconWrong
  93. default:
  94. return ''
  95. }
  96. }
  97. const judgeType = type => {
  98. if (type === 0) {
  99. const typeDescription = '单选题'
  100. const iconType = iconRadio
  101. const iconSelect = iconRadioCorrect
  102. return { typeDescription, iconType, iconSelect }
  103. } else if (type === 1) {
  104. const typeDescription = '多选题'
  105. const iconType = iconCheckout
  106. const iconSelect = iconCheckoutCorrect
  107. return { typeDescription, iconType, iconSelect }
  108. }
  109. }
  110. const judgeLabel = index => {
  111. return String.fromCharCode(65 + index)
  112. }
  113. const ExerciseCard = ({ isFinished, exerciseId, title, type, status, content, options, analysis, handleChangeDate }) => {
  114. const iconResult = judgeResult(status)
  115. const { typeDescription, iconType, iconSelect } = judgeType(type)
  116. let rightResult = ''
  117. return (
  118. <Container>
  119. {iconResult && <Result src={iconResult} />}
  120. <Order>
  121. {title}【{typeDescription}】
  122. </Order>
  123. <Title>{content}</Title>
  124. <div>
  125. {options.map((item, index) => {
  126. const { content, isUserAnswer, isCorrect } = item
  127. const label = judgeLabel(index)
  128. if (isCorrect) {
  129. rightResult += label
  130. }
  131. const changeResult = isFinished ? () => { } : () => handleChangeDate(exerciseId, type, index)
  132. const icon = isUserAnswer ? iconSelect : iconType
  133. return (
  134. <Answer className={classnames({ selected: isUserAnswer })} icon={icon} onClick={changeResult} key={index}>
  135. {label}.{content}
  136. </Answer>
  137. )
  138. })}
  139. </div>
  140. {isFinished && (
  141. <Analysis>
  142. <CorrectAnswer>正确答案:{rightResult}</CorrectAnswer>
  143. <AnswerAnalysis>解析:{analysis}</AnswerAnalysis>
  144. </Analysis>
  145. )}
  146. </Container>
  147. )
  148. }
  149. ExerciseCard.propTypes = {
  150. isFinished: PropTypes.bool,
  151. exerciseId: PropTypes.number,
  152. title: PropTypes.string,
  153. type: PropTypes.number,
  154. status: PropTypes.number,
  155. content: PropTypes.string,
  156. options: PropTypes.array,
  157. analysis: PropTypes.string,
  158. handleChangeDate: PropTypes.func,
  159. }
  160. export default ExerciseCard

中间件

withStyled.js—基础颜色组件

  1. import { forwardRef } from 'react'
  2. import { ThemeProvider, keyframes } from 'styled-components'
  3. export const theme = {
  4. // 基础颜色
  5. primaryColor: 'rgba(255, 221, 0, 1)',
  6. secondaryColor: 'rgba(255, 196, 1, 1)',
  7. linkColor: 'rgba(24, 170, 242, 1)',
  8. borderColor: 'rgba(229, 233, 242, 1)',
  9. backgroundColor: 'rgba(245, 247, 250, 1)',
  10. maskColor: 'rgba(0, 0, 0, 0.5)',
  11. shadowColor: 'rgba(200, 200, 200, 0.05)',
  12. highLightColor: 'rgba(34, 34, 34, 1);',
  13. // 基金相关
  14. increaseColor: '#FC4447',
  15. decreaseColor: '#3CC86B',
  16. // 层级
  17. maskZindex: 100,
  18. popupZindex: 200,
  19. dialogZindex: 300,
  20. // 字体颜色
  21. font100: '#324057',
  22. font200: '#475669',
  23. font300: '#5E6D82',
  24. font400: '#8492A6',
  25. font500: '#99A9BF',
  26. font600: '#C0CCDA',
  27. importantfont: '#FF8026',
  28. // 宽度
  29. maxWidth: '480PX',
  30. // 加载器
  31. loaderPrimaryColor: 'rgba(239, 242, 247, 1)',
  32. loaderSecondaryColor: 'rgba(239, 242, 247, 0.6)'
  33. }
  34. export const autofill = keyframes`
  35. from {
  36. background - color: transparent;
  37. }
  38. to {
  39. background - color: transparent;
  40. }
  41. `
  42. export const popupCenter = keyframes`
  43. 0% {
  44. transform: translateY(-50%) scale(0.8);
  45. }
  46. 100% {
  47. transform: translateY(-50%) scale(1);
  48. }
  49. `
  50. export const riseNavbar = keyframes`
  51. 0% {
  52. bottom: -64px;
  53. }
  54. 100% {
  55. bottom: 0;
  56. }
  57. `
  58. const withStyled = (Component) => {
  59. const StyledComponent = (props, ref) => (
  60. <ThemeProvider theme={theme}>
  61. <Component ref={ref} {...props} />
  62. </ThemeProvider>
  63. )
  64. return forwardRef(StyledComponent)
  65. }
  66. export default withStyled

GlobalModalRich.js—模态框组件

  1. import PropTypes from 'prop-types'
  2. import styled from 'styled-components'
  3. import withStyled from '@@/styles/withStyled'
  4. import { MountPoint } from '@@/utils/dom'
  5. import iconClose from '@@/assets/icons/icon-close.png'
  6. const MaskBasic = withStyled(styled.div`
  7. position: fixed;
  8. left: 0;
  9. right: 0;
  10. top: 0;
  11. bottom: 0;
  12. background-color: ${(props) => props.theme.maskColor};
  13. z-index: ${(props) => props.theme.maskZindex};
  14. `)
  15. const MaskCenter = withStyled(styled.div`
  16. position: absolute;
  17. left: 0;
  18. right: 0;
  19. top: 45%;
  20. transform: translateY(-50%);
  21. padding: 20px;
  22. text-align: center;
  23. z-index: ${(props) => props.theme.dialogZindex};
  24. animation: ${popupCenter} 200ms linear forwards;
  25. `)
  26. const ModalContainer = withStyled(styled.div`
  27. display: inline-block;
  28. margin: 0 auto;
  29. padding: 25px 20px;
  30. max-width: 360px;
  31. min-width: 240px;
  32. width: 86%;
  33. text-align: center;
  34. border-radius: 10px;
  35. background-color: #ffffff;
  36. `)
  37. const ModalTitle = withStyled(styled.h4`
  38. font-size: 20px;
  39. font-weight: bold;
  40. line-height: 1.5;
  41. color: ${(props) => props.theme.font100};
  42. text-align: center;
  43. `)
  44. const ModalContent = withStyled(styled.div`
  45. padding: 30px 0 25px;
  46. `)
  47. const ModalClose = styled.div`
  48. margin: 40px auto 0;
  49. width: 26px;
  50. height: 26px;
  51. background-size: 100% 100%;
  52. background-repeat: no-repeat;
  53. background-image: url(${iconClose});
  54. `
  55. const ModalComponent = ({ title, children, onClick }) => (
  56. <MaskBasic onClick={onClick}>
  57. <MaskCenter>
  58. <ModalContainer onClick={(e) => e.stopPropagation()}>
  59. <ModalTitle>{title}</ModalTitle>
  60. <ModalContent>{children}</ModalContent>
  61. </ModalContainer>
  62. <ModalClose />
  63. </MaskCenter>
  64. </MaskBasic>
  65. )
  66. ModalComponent.defaultProps = {
  67. title: '提示',
  68. children: '请输入提示内容',
  69. onClick: () => { }
  70. }
  71. ModalComponent.propTypes = {
  72. title: PropTypes.string.isRequired,
  73. children: PropTypes.node.isRequired,
  74. onClick: PropTypes.func.isRequired
  75. }
  76. class GlobalModalRich extends MountPoint {
  77. hide = () => {
  78. this.removeMountPoint()
  79. }
  80. show = (options) => {
  81. this.render(<ModalComponent onClick={this.hide} {...options} />)
  82. }
  83. }
  84. export default new GlobalModalRich()