代码题
寄生组合式继承
//父类构造函数
function Father(name){
this.name = name
}
//寄生函数 创建父类原型副本
Father.prototype.eating = function(){}
function inheritPrototype(subType,superType){
var prototype = Object.create(superType.prototype)
prototype.constructor = subType
subType.prototype = prototype
}
//子类构造函数
function Son(name,age){
Father.call(this,name)
this.age= age
}
inheritPrototype(Son,Father)
深拷贝
//方法一
JSON.parse(JSON.stringify(obj))
//方法二
function deepClone(value, map = new WeakMap()){
if(typeof value !== 'object' || value === null ) return value;
if(value instanceof Date || value instanceof RegExp) return value;
if(value instanceof Map){
return value.forEach((item,index)=>{obj.set(index,deepClone(item,map))})
}
if(map.has(value)) return map.get(value)
let res = new value.constructor()
map.set(value,res)
for(let key in value){
if(Object.hasOwn(value,key)){
res[key] = deepClone(value[key],map)
}
}
return res
}
树结构查找
// 数据如下: const tree = [
{ name: "数据1", id: 1, children:
[ { name: "数据2", id: 2, children:
[ { name: "数据3", id: 3, children:
[{ name: "数据4", id: 4, children:
[],
}, ], }, ], }, ], }, ];
//实现
function findNodeById(tree, id) {
if(tree.length) return null
const search =(node)=>{
if(node.id === id){
return node
}else if(node.children){
for(let child of node.children){
const res = search(child)
if(res){
return res
}
}
}
return null
}
for(let root of tree){
const res = search(root)
return res
}
}
//
function findNodeById(tree,id){
if(!tree.length) return null
let res
function search(node){
if(node.id === id) return node
const res = node.children?.length>0 && node.children.find(child => search(child)) || null
}
for(let node of tree){
res = search(node)
if(res !== null) return res
}
return null
}
数组转树
function arrToTree(arr, parentId){
return arr.fifter((val)=>{ val.id === parentId})
.map((val)=>({
...val,children:arrToTree(arr,val.id)
}))
}
树转数组
function treeToArr(tree){
return tree.reduce((pre,next)=>{
return pre.concat(next.children?treeToArr(next.children):next)
},[])
}
//方法二
function treeToArr(tree){
return tree.reduce((prev,cur){
if(!tree.children){
prev.push(cur)
}else{
const list = treeToArr(arr.children)
delete cur.children
prev.push(cur,...list)
}
return prev
},[])
}
数组扁平化
function flatten(arr){
return arr.reduce((pre,next)=>{
return pre.concat(Array.isArray(next)? flatten(next):next)
},[])
}
function flatten(arr) {
while (arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}
数组去重
[...new Set(arr)]
function unique(arr){
return arr.filter((item,index,readonly)=>{
return readonly.indexOf(item,0) === index
})
}
防抖
function useDebounceFn(fn, delay) {
const timeout = useRef(null);
const debounceFn = (...args) => {
if (timeout.current) {
clearTimeout(timeout.current);
}
timeout.current = setTimeout(() => {
fn(...args);
}, delay);
};
useEffect(() => {
if (timeout.current) {
clearTimeout(timeout.current);
}
}, []);
return debounceFn;
}
节流
function useThrottleFn(fn,delay){
const lastTime = useRef(0)
const throttleFn = (..args) => {
const now = Date.now()
if(now - lastTime.current >= wait){
fn(...args)
lastTime.current = now
}
}
return throttleFn
}
instanceof原理
function myInstanceof(left,right){
if(typeof left !== 'object' || left =null) return false
let proto = Object.getPrototypeoOf(left)
while(proto){
if(proto === right.prototype){
return true
}
proto = Object.getProtypeOf(proto)
}
return false
}
new
function myNew(fn){
let newObj = Object.create(fn.prototype)
let res = fn.call(newObj,...args)
return typeof res === 'object' ? res : newObj
}
call ,apply,bind
//call
Function.prototype.myCall = function (thisArg, ...args) {
var fn = this;
thisArg = thisArg ? Object(thisArg) : window;
thisArg.fn = fn;
var res = thisArg.fn(...args);
delete thisArg.fn;
return res;
};
//apply
Function.prototype.myApply = function(thisArg,argsArr){
var fn = this
thisArg = thisArg ? Object(thisArg) : window
thisArg.fn = fn
var res = thisArg.fn(...argsArr)
delete thisArg.fn
return delete
}
//bind
Function.prototype.myBind = function(thisArg,...args){
var fn = this
thisArg = thisArg ? Object(thisArg) : window
var newFn = function(...arg){
thisArg.fn =fn
var newArgs = [...args,...arg]
var res = thisArg.fn(...newArgs)
delete thisArg.fn
return res
}
return newFn
}
实现数字千分符
let num = 12345678910.22;
function parseToMoney(num) {
let [int, decimal] = String.prototype.split.call(num, ".");
let parse = int.replace(/\d(?=(\d{3})+$)/g, function (n) {
return n + ",";
});
let res = decimal ? parse + "." + decimal :parse
return res;
}
let res = parseToMoney(num);
console.log(res);
//方法二
num.toLocalString()
解析 URL Params 为对象
let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';
function(url){
const paramsStr= url.split('?')[1]
const paramsArr = paramsStr.split('&')
let obj = {}
paramsArr.forEach(params =>{
if(/=/.text(params)){
let [key,value] = params.split('=')
val = docodeURIComponent(val)
val = /^\d+$/.test(val) ? parseFloat(val) : val
if(obj.hasOwnProperty(key)){
obj[key] = [].concat(obj[key],value)
}else{
obj[key] = value
}
}else{
obj[key] = null
}
})
return obj
}
function URLParamsObj(url) {
const _url = new URL(url);
const params = {};
for (let [key, value] of _url.searchParams) {
if (params[key]) {
params[key] = [].concat(params[key], value);
} else {
params[key] = value;
}
}
return params;
}
转化为驼峰命名
function tf(str){
return str.replace(/-\w/g,function(s)){
return s.slice(1).toUpperCase()
}
}
函数柯里化
function keli(fn,params){
if(fn.length === 0) return fn
params = params || []
return function curried(...args){
let newArgs = params.concat(args)
if(newArgs >= fn.length){
return fn.apply(this,newArgs)
}else{
return curry.call(this, fn, newArgs)
}
}
}
字符串反转
//方法一
function reveser1(str){
return str.split('').reveser().join('')
}
//方法二
function strReverse(str){
return Array.from(str).reduce((pre,cur)=> return `${cur}${pre}`,'')
}
promise.all
function myAll(proms){
return new Promise((resolve,reject)=>{
res = []
count = 0
proms.forEach((item,index)=>{
count++
Promise.resolve(item).then(data=>{
res[index] = data
if(count === proms.length){
resolve(res)
}
},reject)
})
})
}
promise.race
function myRace(proms){
return new Promise((resolve,reject)=>{
proms.forEach(item=>{
Promise.resolve(item).then(data=>resolve(data),reject)
})
})
}
promise并发控制
async function promisePool(tasks, limit) {
const resArr = new Array(tasks.length); // 初始化结果数组
const queue = []; // 存储当前正在运行的任务
for (let i = 0; i < tasks.length; i++) {
const task = tasks[i]()
.then((res) => {
resArr[i] = res; // 任务成功时,存储结果
})
.catch((err) => {
resArr[i] = err; // 任务失败时,存储错误
})
.finally(() => {
queue.splice(queue.indexOf(task), 1); // 任务完成后,从队列中移除
});
queue.push(task); // 将任务添加到队列中
if (queue.length >= limit) {
// 如果队列已满,等待任意一个任务完成
await Promise.race(queue);
}
}
// 等待所有任务完成
await Promise.allSettled(queue);
// 返回结果数组
return resArr;
}
单例模式
//ES5
let Model = (function(){
let instance
function User(name,age){
this.name = name
this.age = age
}
return function(name){
if(!instance){
return instance = new User()
}
return instance
}
})()
//ES6
class Singleton{
construtor(name,age){
this.name = name
this.age = age
if(!Singleton.instance){
return Singleton.instance
}
return Singleton.instance
}
}
装饰器模式
//js
Function.prototype.before=function(beforeFn){
let _this = this
return function(){
beforeFn.call(this,...args)
return _this.call(this,...args)
}
}
//ts
//类装饰器 ClassDecorator
//属性装饰器 ProtertyDecorator
//参数装饰器 ParameterDecorator
//方法装饰器 MethodDecorator
const Base = (name:string) => {
return const Fn:ClassDecorator(target){
console.log(target,返回的是构造函数)
target.prototype.name = name
}
}
@Base(name)
class Person{
//...
}
观察者模式
class Suject{
constructor(){
this.observers = []
}
add(observer){
this.observers.push(observer)
}
remove(obsever){
this.observers = this.observers.filter(item =>return item !== observer)
}
notify(){
this.observers.forEach(item=> item.update())
}
}
class Observer{
constructor(name){
this.name = name
}
update(){
//...
}
}
const sub = new Subject()
const obs1 = new Observer(obs1)
//订阅
sub.add(obs1)
//发布
sub.notify()
//ts
const List:Set<Function> = new Set()
const autorun = (cb:Function) => {
if(!List.has(cb)){
List.add(cb)
}
}
const observer = <T extends obj>(params:T) =>{
return new Proxy(params,{
set(target,key,value,receiver){
const res = Refect.set(target,key,value,receiver)
List.forEach(fn => fn())
return res
}
})
}
//创建被观察者实例
const obsProxy = observable({ name: "cn" });
//创建观察者并订阅
autorun(() => {
console.log("1");
});
obsProxy.name = "ln";
发布订阅模式
//ts
interface I {
on:(name:string,callback:Function)=>void
once:(name:string,callback:Function)=>void
emit:(name:string,...args:Array<any>)=>void
off:(name:string,callback:Function)=>void
}
class Emitter implements I {
event:Map<string,Function[]>
constructor(){
this.event = new Map()
}
on(name:string,callback:Function){
if(this.event.has(name)){
const callbacks = this.event.get(name)
callback && callbacks.push(callback)
}else{
this.event.set(name,[callback])
}
}
once(name:string,callback:Function){
const cb = (...args:any[])=>{
callback(...args)
this.off(cb)
}
this.on(name,cb)
}
emit(name:string,...args:any[]){
const callbacks = this.event.get(name)
if(callbacks){
callbacks.forEach(Fn => Fn(...args))
}
}
off(name:string,callback:Function){
const callbacks = this.event.get(name)
if(callbacks){
callbacks.splice(callbacks.indexOf(callback),1)
}
}
}
ts泛型工具
将传入的属性变为可选项
type Partial<T> = {
[P in keyof T]?: T[P];
};
将传入的属性变为必填
type Required<T> = {
[P in keyof T]-?: T[P];
};
将传入的属性变为只读
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
定义keey value
type Record<K extends keyof any, T> = {
[P in K]: T;
};
在 T 中,过滤掉非 S 的类型
type Pick<T, K extends keyof T> = {
[key in K]: T[key]
}
从类型T中剔除所有可以赋值给类型U的类型
type Exclude<T, U> = T extends U ? never : T;
算法
字符串压缩
function compressString(str) {
let compressed = '';
let count = 1;
for (let i = 0; i < str.length; i++) {
if (str[i] === str[i + 1]) {
count++;
} else {
compressed += str[i] + (count > 1 ? count : '');
count = 1;
}
}
return compressed.length < str.length ? compressed : str;
}
LRU算法
class LRUCache {
constructor(lenght) {
this.length = lenght; // 存储长度
this.data = new Map(); // 存储数据 }
// 存储数据,通过键值对的方式
set(key, value) {
const data = this.data;
if (data.has(key)) {
data.delete(key)
}
data.set(key, value);
// 如果超出了容量,则需要删除最久的数据
if (data.size > this.length) {
const delKey = data.keys().next().value; data.delete(delKey);
}
}
// 获取数据
get(key) { const data = this.data;
// 未找到
if (!data.has(key)) { return null; }
const value = data.get(key);
// 获取元素
data.delete(key);
// 删除元素
data.set(key, value);
// 重新插入元素
} }
JavaScript 实现钟表上分针与时针夹角
-
计算时针的角度:
- 每小时时针转动 30 度(360 度 / 12 小时)。
- 每分钟时针额外转动 0.5 度(30 度 / 60 分钟)。
- 公式:
时针角度 = (小时 % 12) * 30 + 分钟 * 0.5。
-
计算分针的角度:
- 每分钟分针转动 6 度(360 度 / 60 分钟)。
- 公式:
分针角度 = 分钟 * 6。
-
计算夹角:
- 夹角 =
Math.abs(时针角度 - 分针角度)。 - 如果夹角大于 180 度,取
360 - 夹角(因为钟表的最大角度是 180 度)。
- 夹角 =
function calculateClockAngle(hour, minute) {
// 计算时针角度
const hourAngle = (hour % 12) * 30 + minute * 0.5;
// 计算分针角度
const minuteAngle = minute * 6;
// 计算夹角
let angleDiff = Math.abs(hourAngle - minuteAngle);
// 如果夹角大于 180 度,取 360 - 夹角
if (angleDiff > 180) {
angleDiff = 360 - angleDiff;
}
return angleDiff;
}
有效的括号
给定一个只包括 `'('`,`')'`,`'{'`,`'}'`,`'['`,`']'` 的字符串 `s` ,判断字符串是否有效。
有效字符串需满足:
1. 左括号必须用相同类型的右括号闭合。
1. 左括号必须以正确的顺序闭合。
1. 每个右括号都有一个对应的相同类型的左括号。
function isValid(s){
const stack = []
const map = {
'(' : () => stack.push(')'),
'{' : () => stack.push('}'),
'[' : () => stack.push(']')
}
for(let i in s){
fn = map[s[i]]
top = stack[stack.length-1]
if(fn){
fn()
}else if(top === s[i]){
stack.pop()
}else{
return false
}
}
return true
}
无重复字符的最长子串
function lengthOfLongestSubstring(s){
const set = new Set()
let max = 0
let i=0, j=0
for(i;i<s.length;i++){
if(set.has(s[i])){
set.add(s[i])
max = Math.max(max,set.size)
}else{
while(set.has[i]){
set.delete[j]
j++
}
set.add(s[i])
}
return max
}
}
最长公共前戳
function longestCommonPrefix(strs:Array<string>):string{
const str = strs[0]
while(!strs.every(i => i.startWith(str))){
str= str.slice(0,str.length)
}
return str === ''?'':str
}
最长连续序列
function longestConsecutive(nums:Array<number>):number{
const arr = Array.from(new Set(nums)).sort((a,b)=>a-b)
let ans=1, len=0
for(let i=1;i<arr.length;i++){
if(arr[i] - arr[i-1] === 1){
ans = Math.max(ans,i+1-len)
}else{
len = i
}
}
return ans
}
版本号比较
function compareVersions(version1, version2) {
const v1Parts = version1.split('.')
const v2Parts = version2.split('.')
const maxLength = Math.max(v1Parts.length, v2Parts.length);
for (let i = 0; i < maxLength; i++) {
const num1 = v1Parts[i] || 0;
const num2 = v2Parts[i] || 0;
if (num1 > num2) {
return 1;
} else if (num1 < num2) {
return -1;
}
}
return 0;
}
实现斐波那契数列
function fn (n){
if(n==0) return 0
if(n==1) return 1
return fn(n-2)+fn(n-1)
}
回文数
function isPalindrome(num){
if(num <0 || (num !== 0 && num%10 = 0)) return false
let res = 0
while(num > res){
res = res*10 + num%10
num = Math.floor(num/10)
}
if(num === res || num === Math.floor(res/10)){
return true
}
return false
}
排序
冒泡排序
原理:利用数组的前一项与相邻的后一项相比较,判断大/小,交换位置
const bubbleSort = function(ary){
for(let i = 0; i < ary.length - 1; i++){
for(let j = 0; j < ary.length - 1 - i; j++){
if(ary[j] > ary[j+1]){
[ary[j], ary[j+1]] = [ary[j+1], ary[j]]
}
}
}
return ary
}
选择排序
原理:利用数组的某项与后面所有的值相比较,判断大/小,交换位置
const bubbleSort = function(ary){
for(let i = 0; i < ary.length - 1; i++){
for(let j = i + 1; j < ary.length; j++){
if(ary[i] > ary[j]){
[ary[i], ary[j]] = [ary[j], ary[i]]
}
}
}
return ary
}
原生排序
Array.sort((a, b)=>a-b)
快速排序
原理:取数组的中间值作为基准,判断左右两边的值大或小,添加到相应数组,递归调用,然后将所有的值拼接在一起。
const quick_sort = function(ary){
if(ary.length < 1){
return ary
}
let centerIndex = Math.floor(ary.length/2)
let centerVal = ary.splice(centerIndex, 1)[0]
let left = []
let right = []
ary.forEach(item => {
item > centerVal ? right.push(item) : left.push(item)
})
return [...quick_sort(left), ...[centerVal], ...quick_sort(right)]
}
插入排序
原理:先将数组中的一项添加到新数组中,循环数组每一项与新数组中比较,比较大的值放在后面小的放到新数组的前面。
const insertion_sort = function(ary){
let newAry = ary.splice(0, 1)
for(let i = 0; i < ary.length; i++){
let cur = ary[i]
for(let j = newAry.length - 1; j >= 0;){
if(cur < newAry[j]){
j--
j === -1 && newAry.unshift(cur)
} else {
newAry.splice(j + 1, 0, cur)
j = -1
}
}
}
return [...newAry]
}
react hooks相关练习
实现一个定时器hook 要求每一秒执行一个回调函数
import { useEffect, useRef } from "react";
export default function useInterval(cb: () => void, delay: number) {
const saveCb = useRef<() => void>(null);
//更新最新的回调函数
useEffect(() => {
saveCb.current = cb;
}, [cb]);
//依赖delay设置定时器
useEffect(() => {
function task() {
if (saveCb.current) {
saveCb.current();
}
}
if (delay !== null) {
const id = setInterval(task, delay);
return () => clearInterval(id);
}
}, [delay]);
}
监听dom大小的hook
const useResize = (callback: (width: number, height: number) => void) => {
const ref = useRef(null);
useEffect(() => {
const element = ref.current;
if (element) {
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
callback(entry.contentRect.width, entry.contentRect.height);
}
});
resizeObserver.observe(element);
return () => {
resizeObserver.disconnect();
};
}
}, [callback]);
return ref;
};
react hooks实现select模糊匹配(简单版)
import { ReactNode, useEffect, useState } from "react";
interface FuzzySelectProps {
options: Array<string>;
}
type optionsType = FuzzySelectProps["options"];
const higlightMatch: any = (text: string, search?: string) => {
if (!search) return text;
const regex = new RegExp(`(${search})`, "gi");
return text.split(regex).map((item, index) => {
console.log(item);
return regex.test(item) ? (
<span key={index} style={{ color: "red" }}>
{item}
</span>
) : (
item
);
});
console.log(text.split(regex));
};
export default function FuzzySelect({ options }: FuzzySelectProps) {
const [inputValue, setValue] = useState<string>("");
const [newOptions, setnewOptions] = useState<optionsType>(options);
useEffect(() => {
if (inputValue) {
const filtered = options.filter((option) =>
option.toLowerCase().includes(inputValue.toLowerCase())
);
setnewOptions(filtered);
} else {
setnewOptions(options);
}
}, [inputValue, options]);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value);
};
return (
<div>
<input type="text" value={inputValue} onChange={handleChange} />
<ul>
{newOptions.map((option, index) => {
return <li key={index}>{higlightMatch(option, inputValue)}</li>;
})}
</ul>
</div>
);
}
时分秒倒计时
const Countdown = ({ initialSeconds }) => {
// 使用 useState 来管理剩余秒数
const [seconds, setSeconds] = useState(initialSeconds);
useEffect(() => {
if (seconds > 0) {
// 设置一个定时器,每秒减少一秒
const interval = setInterval(() => {
setSeconds((prevSeconds) => prevSeconds - 1);
}, 1000);
// 在组件卸载或者 seconds 变为 0 时清除定时器
return () => clearInterval(interval);
}
}, [seconds]);
// 将剩余秒数转换为时分秒格式
const formatTime = (time) => {
const hours = Math.floor(time / 3600);
const minutes = Math.floor((time % 3600) / 60);
const secs = time % 60;
const pad = (num) => num.toString().padStart(2, "0");
return `${pad(hours)}:${pad(minutes)}:${pad(secs)}`;
};
return (
<div>
{seconds > 0 ? <p>倒计时: {formatTime(seconds)}</p> : <p>倒计时结束</p>}
</div>
);
};
useAsyncCache
const { useEffect } = require("react");
const { useState } = require("react");
const useAsyncCache = (fn, cache) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const localCache = localStorage.getItem(key);
if (localCache) {
const [value, time] = JSON.parse(localCache);
if (new Date() - time < cache.minutes * 60 * 1000) {
setData(value);
setLoading(false);
return
}
}
const fetch = async () => {
try {
setLoading(true);
const res = await fn();
const localCache = {
value: res,
time: Date.now(),
};
localStorage.setItem(key, JSON.stringify(localCache));
setData(res);
} catch (err) {
} finally {
setLoading(false);
}
};
fetch();
});
return [loading,data]
};
const fetchData = async (time) =>
new Promise((resolve) => {
setTimeout(() => {
console.log("fetchData");
resolve("这是结果");
}, time);
});
// 使用示例
const App = () => {
const { loading, data } = useAsyncCache(fetchData, { key: "", minutes: 1 });
if (loading) {
return <div>Loading</div>;
}
return <div>{data}</div>;
};
loadsh 参数查找
function get(obj, path, defaultValue) {
const keys = path.replace(/[(\d+)]/g, '.$1').split('.');
let current = obj;
for (let key of keys) {
if (current === null || current === undefined) {
return defaultValue;
}
current = current[key];
}
return current === undefined ? defaultValue : current;
}
// 示例使用
let obj = {a: [{b: {c: 3}}]};
console.log(get(obj, "a[0].b.c")); // => 3
console.log(get(obj, "a.0.b.c")); // => 3
console.log(get(obj, "a.b.c", "default")); // => "
登录组件
import { Button, Form, Input, Space } from "antd";
import "./index.css";
import { useForm } from "antd/es/form/Form";
import { use, useState } from "react";
function register(options: any) {
return new Promise<void>((resolve, reject) => {
setTimeout(() => {
localStorage.setItem("login", JSON.stringify(options));
resolve();
}, 2000);
});
}
function login() {
return new Promise((resolve) => {
setTimeout(() => {
const res = JSON.parse(localStorage.getItem("login")!);
console.log(res);
resolve(res);
}, 2000);
});
}
export default function Authing() {
const [form] = useForm();
const [active, setActive] = useState("login");
const handleFinish = async () => {
const value = form.getFieldsValue();
if (active === "login") {
try {
const loginData: any = await login();
if (
loginData.user === value.user &&
loginData.password === value.password
) {
console.log("登录成功");
} else {
console.log("登录失败");
}
} catch (error) {
console.log(error);
}
} else {
await register(value);
console.log("注册成功");
}
};
const activeClick = () => {
setActive((value) => {
return value === "login" ? "register" : "login";
});
form.resetFields();
};
return (
<>
<div className="form">
<Form
name="login"
form={form}
labelCol={{ span: 8 }}
wrapperCol={{ span: 16 }}
onFinish={handleFinish}
>
<Form.Item
name="user"
label="user"
rules={[{ required: true, message: "请输入用户名" }]}
>
<Input />
</Form.Item>
<Form.Item
name="password"
label="password"
rules={[{ required: true, message: "请输入密码" }]}
>
<Input.Password />
</Form.Item>
{active === "register" ? (
<Form.Item
name="confirm"
label="Confirm Password"
rules={[
{ required: true, message: "请输入" },
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue("password") === value) {
return Promise.resolve();
} else {
return Promise.reject(new Error("密码不一致"));
}
},
}),
]}
>
<Input />
</Form.Item>
) : null}
<Form.Item wrapperCol={{ offset: 6, span: 16 }}>
<Space>
<Button type="primary" htmlType="submit">
{active === "login" ? "登录" : "注册"}
</Button>
<Button type="link" onClick={activeClick}>
是否{active === "login" ? "注册" : "登录"}
</Button>
</Space>
</Form.Item>
</Form>
</div>
</>
);
}
实现简单todolist
import React, { useState } from 'react';
const TodoList = () => {
const [tasks, setTasks] = useState([]);
const [inputValue, setInputValue] = useState('');
const handleInputChange = (e) => {
setInputValue(e.target.value);
};
const handleAddTask = () => {
if (inputValue.trim() !== '') {
setTasks([...tasks, inputValue]);
setInputValue('');
}
};
const handleDeleteTask = (index) => {
const newTasks = [...tasks];
newTasks.splice(index, 1);
setTasks(newTasks);
};
return (
<div className="p-4">
<h1 className="text-2xl font-bold mb-4">ToDo List</h1>
<div className="flex mb-4">
<input
type="text"
value={inputValue}
onChange={handleInputChange}
className="border border-gray-300 rounded-l-md p-2 w-full"
placeholder="Add a new task"
/>
<button
onClick={handleAddTask}
className="bg-blue-500 text-white rounded-r-md p-2"
>
Add
</button>
</div>
<ul>
{tasks.map((task, index) => (
<li key={index} className="flex items-center mb-2">
<span className="mr-2">{task}</span>
<button
onClick={() => handleDeleteTask(index)}
className="text-red-500"
>
Delete
</button>
</li>
))}
</ul>
</div>
);
};
export default TodoList;