# 页面间传值

# 父传子

使用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()