前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >React 组件:计时器例子

React 组件:计时器例子

作者头像
Michael阿明
发布2022-07-28 08:33:58
4.3K0
发布2022-07-28 08:33:58
举报
文章被收录于专栏:Michael阿明学习之路

文章目录

learn from 《React全家桶:前端开发与实例详解》 https://zh-hans.reactjs.org/tutorial/tutorial.html https://zh-hans.reactjs.org/docs/create-a-new-react-app.html#create-react-app

本章学习的目标是做一个定时器

1. 将应用程序分解为组件

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. 构建应用静态版本

app.js

代码语言:javascript
复制
class TimersDashBoard extends React.Component {
    render() {
        return (
            <div className='ui three column centered grid'>
                <div className='column'>
                    <EditableTimerList/>
                    <ToggleableTimerForm isOpen={true}/>
                </div>
            </div>
        )
    }
}

class EditableTimerList extends React.Component {
    render() {
        return (
            <div id='timers'>
                <EditableTimer
                    title='学习react'
                    project='react web'
                    elasped='3600'
                    runningSince={null}
                    editFormOpen={false}
                />
                <EditableTimer
                    title='学习python'
                    project='deep learning'
                    elasped='3600'
                    runningSince={null}
                    editFormOpen={true}
                />
            </div>
        )
    }
}

class EditableTimer extends React.Component {
    render() {
        if (this.props.editFormOpen) {
            return (
                <TimerForm
                    title={this.props.title}
                    project={this.props.project}
                />
            )
        } else {
            return (
                <Timer
                    title={this.props.title}
                    project={this.props.project}
                    elasped={this.props.elasped}
                    runningSince={this.props.runningSince}
                />
            )
        }
    }
}

class TimerForm extends React.Component {
    render() {
        const submitText = this.props.title ? 'Update' : 'Create'
        return (
            <div className='ui centered card'>
                <div className='content'>
                    <div className='ui form'>
                        <div className='field'>
                            <label>Title</label>
                            <input type='text' defaultValue={this.props.title}/>
                        </div>
                        <div className='field'>
                            <label>Project</label>
                            <input type='text' defaultValue={this.props.project}/>
                        </div>
                        <div className='ui two bottom attached buttons'>
                            <button className='ui basic blue button'>
                                {submitText}
                            </button>
                            <button className='ui basic red button'>
                                Cancel
                            </button>
                        </div>
                    </div>
                </div>
            </div>
        )
    }
}

class ToggleableTimerForm extends React.Component {
    render() {
        if (this.props.isOpen) {
            return (
                <TimerForm/>
            )
        } else {
            return (
                <div className='ui basic content center aligned segment'>
                    <button className='ui basic button icon'>
                        <i className='plus icon'/>
                    </button>
                </div>
            )
        }
    }
}

class Timer extends React.Component {
    render() {
        const elaspedString = helpers.renderElapsedString(this.props.elasped)
        return (
            <div className='ui centered card'>
                <div className='content'>
                    <div className='header'>
                        {this.props.title}
                    </div>
                    <div className='meta'>
                        {this.props.project}
                    </div>
                    <div className='center aligned description'>
                        <h2> {elaspedString} </h2>
                    </div>
                    <div className='extra content'>
                        <span className='right floated edit icon'>
                            <i className='edit icon'/>
                        </span>
                        <span className='right floated trash icon'>
                            <i className='trash icon'/>
                        </span>
                    </div>
                </div>
                <div className='ui bottom attached blue basic button'>
                    Start
                </div>
            </div>
        )
    }
}

ReactDOM.render(
    <TimersDashBoard/>,
    document.getElementById('content')
)
在这里插入图片描述
在这里插入图片描述

<ToggleableTimerForm isOpen={true}/> 改成 falseToggleableTimerFormrender 回返回 显示 plus icon 的分支

在这里插入图片描述
在这里插入图片描述

3. 哪些组件是有状态的

  • 是通过 props 从 父组件 传递进来的吗?是的话,它很可能不是 state
  • 不随时间变化,很可能不是 state
  • 可以根据其他 state 或 props 计算得到,不是 state

原则就是用尽可能少的 state

4. 每个 state 应该在哪个组件里

如何确定,步骤:

  • 标识基于该state渲染的组件
  • 查找state共同所有者
  • 较高层次的组件拥有该state
  • 找不到的话,创建新组件来保存 state,并置于层次结构的上方

5. 硬编码初始化state

props 可理解为 : 不可改变的 state

  • TimersDashBoard
代码语言:javascript
复制
class TimersDashBoard extends React.Component {
    state = {
        timers: [
            {
                title: 'react learning',
                project: 'web dev',
                id: uuid.v4(),
                elasped: 3600 * 1000,
                runningSince: Date.now(),
            },
            {
                title: 'python learning',
                project: 'ai dev',
                id: uuid.v4(),
                elasped: 30 * 1000,
                runningSince: null,
            }
        ]
    }

    render() {
        return (
            <div className='ui three column centered grid'>
                <div className='column'>
                    <EditableTimerList
                        timers={this.state.timers}
                    />
                    <ToggleableTimerForm isOpen={false}/>
                </div>
            </div>
        )
    }
}

class EditableTimerList extends React.Component {
    render() {
        const timers = this.props.timers.map(
            (timer) => (
                <EditableTimer
                    key={timer.id}
                    id={timer.id}
                    title={timer.title}
                    project={timer.project}
                    elasped={timer.elasped}
                    runningSince={timer.runningSince}
                />
            )
        )
        return (
            <div id='timers'>
                {timers}
            </div>
        )
    }
}
  • 表单是有状态的
代码语言:javascript
复制
class EditableTimer extends React.Component {
    state = {
        editFormOpen: false,
    }
    render() {
        if (this.state.editFormOpen) {
            return (
                <TimerForm
                    id={this.props.id}
                    title={this.props.title}
                    project={this.props.project}
                />
            )
        } else {
            return (
                <Timer
                    id={this.props.id}
                    title={this.props.title}
                    project={this.props.project}
                    elasped={this.props.elasped}
                    runningSince={this.props.runningSince}
                />
            )
        }
    }
}

class TimerForm extends React.Component {
    state = {
        title: this.props.title || '',
        project: this.props.project || '',
    }
    handleTitleChange = (e) => {
        this.setState({title: e.target.value})
    }
    handleProjectChange = (e) => {
        this.setState({project: e.target.value})
    }
    render() {
        const submitText = this.props.title ? 'Update' : 'Create'
        return (
            <div className='ui centered card'>
                <div className='content'>
                    <div className='ui form'>
                        <div className='field'>
                            <label>Title</label>
                            <input type='text' value={this.state.title}
                                onChange={this.handleTitleChange}/>
                        </div>
                        <div className='field'>
                            <label>Project</label>
                            <input type='text' value={this.state.project}
                                   onChange={this.handleProjectChange}/>
                        </div>
                        <div className='ui two bottom attached buttons'>
                            <button className='ui basic blue button'>
                                {submitText}
                            </button>
                            <button className='ui basic red button'>
                                Cancel
                            </button>
                        </div>
                    </div>
                </div>
            </div>
        )
    }
}

class ToggleableTimerForm extends React.Component {
    state = {
        isOpen: false,
    }
    handleFormOpen = () => {
        this.setState({isOpen: true});
    }
    render() {
        if (this.state.isOpen) {
            return (
                <TimerForm/>
            )
        } else {
            return (
                <div className='ui basic content center aligned segment'>
                    <button
                        className='ui basic button icon'
                        onClick={this.handleFormOpen}>
                        <i className='plus icon'/>
                    </button>
                </div>
            )
        }
    }
}

6. 添加反向数据流

TimerForm组件

  • 表单创建、更新计时器
  • 取消动作

让父组件拥有函数(在事件发生时决定采取什么行为),父组件 通过 props 将函数传递给 TimerForm

代码语言:javascript
复制
class TimerForm extends React.Component {
    state = {
        title: this.props.title || '',
        project: this.props.project || '',
    }
    handleTitleChange = (e) => {
        this.setState({title: e.target.value})
    }
    handleProjectChange = (e) => {
        this.setState({project: e.target.value})
    }
    handleSubmit = () => {
        this.props.onFormSubmit({
            id: this.props.id,
            title: this.state.title,
            project: this.state.project,
        })
    }
    render() {
        const submitText = this.props.id ? 'Update' : 'Create'
        return (
            <div className='ui centered card'>
                <div className='content'>
                    <div className='ui form'>
                        <div className='field'>
                            <label>Title</label>
                            <input type='text' value={this.state.title}
                                onChange={this.handleTitleChange}/>
                        </div>
                        <div className='field'>
                            <label>Project</label>
                            <input type='text' value={this.state.project}
                                   onChange={this.handleProjectChange}/>
                        </div>
                        <div className='ui two bottom attached buttons'>
                            <button className='ui basic blue button'
                                    onClick={this.handleSubmit}>
                                {submitText}
                            </button>
                            <button className='ui basic red button'
                                    onClick={this.props.onFormClose}>
                                Cancel
                            </button>
                        </div>
                    </div>
                </div>
            </div>
        )
    }
}
  • ToggleableTimerForm 组件
代码语言:javascript
复制
class ToggleableTimerForm extends React.Component {
    state = {
        isOpen: false,
    }
    handleFormOpen = () => {
        this.setState({isOpen: true});
    }
    handleFormClose = () => {
        this.setState({isOpen: false});
    }
    handleFormSubmit = (timer) => {
        this.props.onFormSubmit(timer);
        this.setState({
            isOpen: false,
        })
    }
    render() {
        if (this.state.isOpen) {
            return (
                <TimerForm
                    onFormSubmit={this.handleFormSubmit}
                    onFormClose={this.handleFormClose}
                />
            )
        } else {
            return (
                <div className='ui basic content center aligned segment'>
                    <button
                        className='ui basic button icon'
                        onClick={this.handleFormOpen}>
                        <i className='plus icon'/>
                    </button>
                </div>
            )
        }
    }
}
  • TimersDashBoard 组件
代码语言:javascript
复制
	handleCreateFormSubmit = (timer) => {
        this.createTimer(timer);
    }
    createTimer = (timer) => {
        const t = helpers.newTimer(timer);
        this.setState({
            timers: this.state.timers.concat(t),
        })
    }
	render() {
	        return (
	            <div className='ui three column centered grid'>
	                <div className='column'>
	                    <EditableTimerList
	                        timers={this.state.timers}/>
	                    <ToggleableTimerForm
	                        onFormSubmit={this.handleCreateFormSubmit}/>
	                </div>
	            </div>
	        )
	    }
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

还不能开始,删除,修改

  • 更新计时器 编辑 Timer 组件,添加函数 onEditClick
代码语言:javascript
复制
						<span
                            className='right floated edit icon'
                            onclick={this.props.onEditClick}>
                            <i className='edit icon'/>
                        </span>
  • EditableTimer 组件
代码语言:javascript
复制
	state = {
        editFormOpen: false,
    }
    handleEditClick = () => {
        this.openForm()
    }
    handleFormClose = () => {
        this.closeForm()
    }
    handleSubmit = (timer) => {
        this.props.onFormSubmit(timer);
        this.closeForm();
    }
    closeForm = () => {
        this.setState({editFormOpen: false});
    }
    openForm = () => {
        this.setState({editFormOpen: true});
    }

这些函数作为 props 向下传递

代码语言:javascript
复制
	render() {
        if (this.state.editFormOpen) {
            return (
                <TimerForm
                    id={this.props.id}
                    title={this.props.title}
                    project={this.props.project}
                    onFormSubmit={this.handleFormSubmit}
                    onFormClose={this.handleFormClose}
                />
            )
        } else {
            return (
                <Timer
                    id={this.props.id}
                    title={this.props.title}
                    project={this.props.project}
                    elapsed={this.props.elapsed}
                    runningSince={this.props.runningSince}
                    onEditClick={this.handleEditClick}
                />
            )
        }
    }
  • EditableTimerList 添加 onFormSubmit={this.props.onFormSubmit}
代码语言:javascript
复制
class EditableTimerList extends React.Component {
    render() {
        const timers = this.props.timers.map(
            (timer) => (
                <EditableTimer
                    key={timer.id}
                    id={timer.id}
                    title={timer.title}
                    project={timer.project}
                    elapsed={timer.elapsed}
                    runningSince={timer.runningSince}
                    onFormSubmit={this.props.onFormSubmit}
                />
            )
        )
        return (
            <div id='timers'>
                {timers}
            </div>
        )
    }
}
  • 顶层组件 TimersDashBoard
代码语言:javascript
复制
	handleEditFormSubmit = (attrs) => {
        this.updateTimer(attrs);
    }
    updateTimer = (attrs) => {
        this.setState({
            timers: this.state.timers.map((timer) => {
                if (timer.id === attrs.id) {
                    return Object.assign({}, timer,{
                        title: attrs.title,
                        project: attrs.project,
                    })
                }
                else{
                    return timer;
                }
            })
        })
    }

属性传递给 EditableTimerList

代码语言:javascript
复制
					<EditableTimerList
                        timers={this.state.timers}
                        onFormSubmit={this.handleEditFormSubmit}
                    />

删除计时器

Timer

代码语言:javascript
复制
	handleTrashClick = () => {
        this.props.onTrashClick(this.props.id)
    }
	。。。
						<span className='right floated trash icon'
                            onClick={this.this.handleTrashClick}>
                            <i className='trash icon'/>
                        </span>

向上传递 EditableTimer

代码语言:javascript
复制
		else {
            return (
                <Timer
                    id={this.props.id}
                    title={this.props.title}
                    project={this.props.project}
                    elapsed={this.props.elapsed}
                    runningSince={this.props.runningSince}
                    onEditClick={this.handleEditClick}
                    onTrashClick={this.props.onTrashClick} // add
                />
            )
        }

向上传递 EditableTimerList

代码语言:javascript
复制
			<EditableTimer
                    key={timer.id}
                    id={timer.id}
                    title={timer.title}
                    project={timer.project}
                    elapsed={timer.elapsed}
                    runningSince={timer.runningSince}
                    onFormSubmit={this.props.onFormSubmit}
                    onTrashClick={this.props.onTrashClick}  // add
                />

TimersDashBoard 顶层实现这个删除函数

代码语言:javascript
复制
	handleTrashClick = (timerId) => {
        this.deleteTimer(timerId);
    }
    deleteTimer = (timerId) => {
        this.setState({
            timers: this.state.timers.filter(t => t.id !== timerId)
        })
    }
    。。。
					<EditableTimerList
                        timers={this.state.timers}
                        onFormSubmit={this.handleEditFormSubmit}
                        onTrashClick={this.handleTrashClick} // add
                    />
在这里插入图片描述
在这里插入图片描述
  • 计时器还不能工作

Timer组件

代码语言:javascript
复制
	componentDidMount() {
        this.forceUpdateInterval = setInterval(() => this.forceUpdate(), 50)
    } // 每 50 ms 调用一次 forceUpdate,重新渲染
    componentWillUnmount() {
        clearInterval(this.forceUpdateInterval)
    }  // 停止 forceUpdateInterval 的间隔执行,计时器删除之前调用

    handleTrashClick = () => {
        this.props.onTrashClick(this.props.id)
    }

    render() {
        const elapsedString = helpers.renderElapsedString(this.props.elapsed, this.props.runningSince)
        return (
        。。。
        )
    }

添加启动,停止

代码语言:javascript
复制
	handleStartClick = () => {
        this.props.onStartClick(this.props.id)
    }

    handleStopClick = () => {
        this.props.onStopClick(this.props.id)
    }

在 Timer 的 return 最底部添加 新组件

代码语言:javascript
复制
				<TimerActionButton
                    timerIsRunning={!!this.props.runningSince}
                    onStartClick={this.handleStartClick}
                    onStopClick={this.handleStopClick}
                />
  • TimerActionButton
代码语言:javascript
复制
class TimerActionButton extends React.Component {
    render() {
        if (this.props.timerIsRunning) {
            return (
                <div
                    className='ui bottom attached red basic button'
                    onClick={this.props.onStopClick}
                >
                    Stop
                </div>
            )
        } else {
            return (
                <div
                    className='ui bottom attached green basic button'
                    onClick={this.props.onStartClick}
                >
                    Start
                </div>
            )
        }
    }
}

这些事件需要在上层结构中传递

EditableTimer

代码语言:javascript
复制
				<Timer
                    id={this.props.id}
                    title={this.props.title}
                    project={this.props.project}
                    elapsed={this.props.elapsed}
                    runningSince={this.props.runningSince}
                    onEditClick={this.handleEditClick}
                    onTrashClick={this.props.onTrashClick}
                    onStartClick={this.props.onStartClick} // add
                    onStopClick={this.props.onStopClick} // add
                />

EditableTimerList

代码语言:javascript
复制
				<EditableTimer
                    key={timer.id}
                    id={timer.id}
                    title={timer.title}
                    project={timer.project}
                    elapsed={timer.elapsed}
                    runningSince={timer.runningSince}
                    onFormSubmit={this.props.onFormSubmit}
                    onTrashClick={this.props.onTrashClick}
                    onStartClick={this.props.onStartClick} // add
                    onStopClick={this.props.onStopClick} // add
                />

TimersDashBoard

代码语言:javascript
复制
	handleStartClick = (timerId) => {
        this.startTimer(timerId);
    }

    startTimer = (timerId) => {
        const now = Date.now();
        this.setState({
            timers: this.state.timers.map((timer) => {
                if(timer.id === timerId) {
                    return Object.assign({}, timer, {
                        runningSince: now
                    })
                }else{
                    return timer;
                }
            })
        })
    }

    handleStopClick = (timerId) => {
        this.stopTimer(timerId);
    }

    stopTimer = (timerId) => {
        const now = Date.now();
        this.setState({
            timers: this.state.timers.map((timer) => {
                if(timer.id === timerId) {
                    const lastElapsed = now - timer.runningSince;
                    return Object.assign({}, timer,{
                        elapsed: timer.elapsed + lastElapsed,
                        runningSince: null
                    })
                } else {
                    return timer;
                }
            })
        })
    }

。。。

					<EditableTimerList
                        timers={this.state.timers}
                        onFormSubmit={this.handleEditFormSubmit}
                        onTrashClick={this.handleTrashClick}
                        onStartClick={this.handleStartClick} //add
                        onStopClick={this.handleStopClick}  //add
                    />
在这里插入图片描述
在这里插入图片描述

7. 添加服务器通信

上面的计时器状态不可以保存,需要保存在服务器上

见下一章

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-07-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 1. 将应用程序分解为组件
  • 2. 构建应用静态版本
  • 3. 哪些组件是有状态的
  • 4. 每个 state 应该在哪个组件里
  • 5. 硬编码初始化state
  • 6. 添加反向数据流
  • 7. 添加服务器通信
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档