react1-创建样式、基本语法、样式、常用事件、useState()、计算器案例及购物车案例

react

创建项目

npm create vite@latest 选择react、JavaScript

npm i 创建依赖包

npm run dev 运行


项目架构

企业微信截图_20250819171436

删除CSS文件、public目录、assets目录


第一个react项目

JSX:javascript xml 不是真正的 HTML,而是一种语法糖,最终会被编译成普通的 JavaScript

删除App.jsx中的所有代码,重写:

import Greet from "./components/greet"

const App = () => {
  return (
    <div>
      <Greet/>
    </div>
  )
}

export default App
const Greet = () => {
    return <h1>yaunyu</h1>
}

export default Greet

插件

ES7 React/Redux/GraphQL/React-Native

安装后通过:rafce可以快速写出jsx的代码模板


语法

基础语法

  • 一定记得export否则报错

  • 函数的第一个字母必须大写

  • 通过return只能返回一个父HTML元素

  • return的元素必须闭合

  • jsx的属性是驼峰:

    • 类不是class而是className

    • form表单的label不是for而是htmlFor

  • { }中的内容是表达式,可进行计算,如{ 2 + 2 }显示的是4,可调用方法

    不仅可以写在标签内,也可以写在className内

  • ()中的内容是html标签


渲染数组

渲染数组时,标签中必须使用key来标识

const App = () => {
  const userInfo = [
    {
      name: 'Jack',
      age: 12,
      country: 'China'
    },
    {
      name: 'Mick',
      age: 16,
      country: 'China'
    },
    {
      name: 'Jone',
      age: 19,
      country: 'China'
    }
  ]

  return (
    <div>
      {
        userInfo.map(({name, age, country}) =>
        (
          <ul key={Math.random()}>
            <li>{name}</li>
            <li>{age}</li>
            <li>{country}</li>
          </ul>
        )
        )
      }
    </div>
  )
}

export default App



条件渲染

// 父组件
import Password from './components/Password'

const App = () => {
  return (
    <Password isValid = {true}/>
  )
}

export default App

// 子组件①
const valid = 'This is valid password.'
const invalid = 'This is invalid password.'

const Password = ({isValid}) => {
  return (
    <div>
      {
        isValid ? <h1>{valid}</h1> : <h1>{invalid}</h1>
      }
    </div>
  )
}

export default Password
// 子组件②
const valid = 'This is valid password.'
const invalid = 'This is invalid password.'

const Password = ({isValid}) => {
    if (isValid) return <h1> {valid} </h1>
    else return <h1> {invalid} </h1>
}

export default Password
const App = () => {
  const item = ['wireless earbuds', 'power bank', 'new ssd', 'hoddies']
  return (
    <>
      <h1>Cart🛒</h1>
      {item.length > 0 && <h2>You have {item.length} items in your cart.</h2>}
      <h1>products👇</h1>
      <ul>
        {
          item.map(i => (
            <li key={Math.random()}>{i}</li>
          ))
        }
      </ul>
    </>
  )
}

export default App


props传递参数

允许父组件使用props向子组件传递参数

// 父组件
import User from './components/User'

const App = () => {
  return (
    <User
      img = 'https://i.cetsteam.com/imgs/2025/08/21/7af16499253b4c06.jpg'
      name = 'yuanyu'
      age = { 24 }
      isMarried = { false }
      hobbies = {['sleeping', 'playing', 'reading']}
    />
  )
}

export default App

// 子组件
const User = (props) => {
  return (
    <div>
      <img src = {props.img} alt = { props.name } />
      <h2>name: { props.name }</h2>
      <h2>age: { props.age }</h2>
      <h2>isMarried: { props.isMarried }</h2>
      <h2>hobbies: { props.hobbies }</h2>
    </div>
  )
}

export default User

// 子组件解构
const User = ({img, name, age, isMarried, hobbies}) => {
  return (
    <div>
      <img src = {img} alt = { name } width={200}/>
      <h2>name: { name }</h2>
      <h2>age: { age }</h2>
      <h2>isMarried: { isMarried }</h2>
      <h2>hobbies: { hobbies }</h2>
    </div>
  )
}

export default User


样式

行内样式

语法:style = {{ 属性名: '属性值' }}

const App = () => {
	return <> <h1 style = {{ color:'red' }}>hello</h1> </>
}
export default App

内部样式

语法:xxstyle = { 属性名: '属性值' } <标签 style = {xxstyle }>

const separateStyle = {color:'red', backgroundColor: 'skyblue', padding: '20px'}
const App = () => {
	return <> <h1 style = {separateStyle}>Separate Style</h1> </>
}
export default App

外部样式

import './index.css'

const App = () => {

  return (
    <>
      <h1>Outside Style</h1>
    </>
  )
}

export default App

*{
    margin: 0;
    padding: 0;
}

body{
    background-color: antiquewhite;
}

h1{
    color:blueviolet
}

icon

网址:React Icons

安装:npm install react-icons --save

使用:

import { BsFillBalloonHeartFill } from "react-icons/bs";

const IconComponent = () => {
  return (
    <div>
      <BsFillBalloonHeartFill size={60} color="pink"/>
    </div>
  )
}

export default IconComponent

事件

const handleCopy = () => {
    console.log('thief!')
}

const Copy = () => {
  return (
    <div onCopy={handleCopy}>
        春江潮水连海平,海上明月共潮生。
        滟滟随波千万里,何处春江无月明。
        江流宛转绕芳甸,月照花林皆似霰。
        空里流霜不觉飞,汀上白沙看不见。
        江天一色无纤尘,皎皎空中孤月轮。
        江畔何人初见月?江月何年初照人?
        人生代代无穷已,江月年年望相似。
        不知江月待何人,但见长江送流水。
        白云一片去悠悠,青枫浦上不胜愁。
        谁家今夜扁舟子?何处相思明月楼?
        可怜楼上月裴回,应照离人妆镜台。
        玉户帘中卷不去,捣衣砧上拂还来。
        此时相望不相闻,愿逐月华流照君。
        鸿雁长飞光不度,鱼龙潜跃水成文。
        昨夜闲潭梦落花,可怜春半不还家。
        江水流春去欲尽,江潭落月复西斜。
        斜月沉沉藏海雾,碣石潇湘无限路。
        不知乘月几人归,落月摇情满江树。
    </div>
  )
}

export default Copy
import Copy from './components/Copy'

const App = () => {

  return (
    <>
      <Copy/>
    </>
  )
}

export default App

常用事件

事件 触发条件
onClick 鼠标点击元素时触发
onDoubleClick 鼠标双击元素时触发
onMouseEnter / onMouseLeave 鼠标进入 / 离开元素时触发(不冒泡)
onMouseDown / onMouseUp 鼠标按下 / 松开时触发
onKeyDown / onKeyUp 按下 / 松开键盘按键时触发
onChange 表单元素值发生变化时触发
onSubmit 表单提交时触发
onFocus / onBlur 元素获取 / 失去焦点时触发
onScroll 元素滚动时触发
onCopy 复制时触发

hooks

state:用于储存和管理数据

react16.8的新功能,可以在不编写类组件的情况下使用state

允许在函数组件中管理状态


useState()

可以在组件中追踪数据和属性

const [data, changeData] = useState()

  • data:初始值
  • changeData:改变值的函数

wechat_2025-08-21_153156_403

import { useState } from "react"

const App = () => {
  const [count, setCount] = useState(0)
  const increment = () => setCount(count + 1)
  const decrement = () => setCount(count - 1)

  return <>
    <h1>{count}</h1>
    <button onClick={increment}>+</button>
    <button onClick={decrement}>-</button>
  </>
}

export default App

使用set方法改变的值会使得应用重新渲染,如果直接改变值不会重新渲染

import { useState } from "react"

const App = () => {
  const [games, setGames] = useState(['一梦江湖', '逆水寒', '燕云十六声'])

  const addNew = () => setGames([...games,'以闪亮之名'])
  const moveGame = () => setGames(games.slice(0,-1))
  const updateGame = () => setGames(['一梦江湖1', '逆水寒', '燕云十六声'])

  return <>
    {
      games.map(game => {
        return <li key={Math.random()}>{game}</li>
      })
    }
    <button onClick={addNew}>add new game</button>
    <button onClick={moveGame}>move a game</button>
    <button onClick={updateGame}>update a game</button>
  </>
}

export default App

父子组件通信

import { useState } from "react"
import CountOne from "./components/CountOne"
import CountTwo from "./components/CountTwo"

const App = () => {
  const [count, setCount] = useState(0)

  return <>
    <CountOne count={count} onClickHandler={() => setCount(count + 1)}/>
    <CountTwo count={count} onClickHandler={() => setCount(count + 1)}/>
  </>
}

export default App
const CountOne = ({count, onClickHandler}) => {
  return (
    <div>
      <h1>{count}</h1>
      <button onClick={onClickHandler}>increment</button>
    </div>
  )
}

export default CountOne

注意:setCount中只能写count+1而不是count++

  • count + 1表达式,返回的是 count 加 1 后的新值(不会修改原变量)
    例如:count = 0 时,count + 1 返回 1count 本身仍为 0
  • count ++自增语句,特点是先返回 count当前值,再将 count 加 1(会修改原变量)

双向绑定

import { useState, useEffect } from "react"

const CountTwo = () => {
  const [name,  setName] = useState(() => {
    const saveName = localStorage.getItem('name') || ''
    saveName ? JSON.parse(saveName) : ''
  })

  useEffect(()=>{
    localStorage.setItem('name',JSON.stringify(name))
  },[name])

  const changeHandler = (e) => {
    setName(e.target.value)
  }

  const clearHandler = () => {
    setName('')
    localStorage.removeItem('name')
  }

  return (
    <div>
      <h1>your name: {name}</h1>
      <input type="text" value={name} onChange={changeHandler} placeholder="enter your name"/>
      <button onClick={clearHandler}>clear name</button>
    </div>
  )
}

export default CountTwo
import CountTwo from "./components/CountTwo"

const App = () => {
  return <>
    <CountTwo/>
  </>
}

export default App

企业微信截图_20250825092513

三处可实现响应式


数组
import { useState } from "react"


const TodoList = () => {
    const [item, setItem] = useState('')
    const [todoList, setTodoList] = useState([])

    const addHandler = (e) => {
        e.preventDefault()
        if (item.trim()) {
            setTodoList([...todoList, item])           
            setItem('')
        }
    }

    const changeHandler = (e) => {
        setItem(e.target.value)
    }

    return (
        <div>
            <form action="#" onSubmit={addHandler}>
                代办项输入:<input type="text" value={item} onChange={changeHandler} />
                <button type="submit">增加代办项</button>
            </form>
            <h1>代办项展示</h1>
            <ul>
                {
                    todoList.map((todoItem, index) => {
                        return <li key={index}>{todoItem}</li>
                    })
                }
            </ul>
        </div>
    )
}

export default TodoList

企业微信截图_20250825155049


对象
import { useState } from 'react'

const Profile = () => {
    const [info, setInfo] = useState({
        name: '',
        age: ''
    })

    const handleChange = (e) => {
        const {name, value} = e.target
        setInfo({
            ...info,
            [name]: value,
        })
    }

    return (
        <div>
            <h1>profile information</h1>
            <div>
                <label htmlFor="name">Name:
                    <input type="text" value={info.name} name='name' onChange={handleChange}/>
                </label>
            </div>
            
            <div>
                <label htmlFor="age">Age:
                    <input type="text" value={info.age} name='age' onChange={handleChange}/>
                </label>
            </div>

            <div>
                <h2>Name: {info.name}</h2>
                <h2>Age: {info.age}</h2>
            </div>
        </div>
    )
}

export default Profile

wechat_2025-08-25_161008_101


数组对象
import { useState } from 'react'

const ShoppingList = () => {
    const [shoppingList, setShoppingList] = useState([])
    const [name, setName] = useState('')
    const [quantity, setQuantity] = useState('')

    const handleSubmit = (e) => {
        e.preventDefault()
        setShoppingList([...shoppingList,{name, quantity}])
    }

    const handleChange = (e) => {
        const {name, value} = e.target
        if (name === 'name') setName(value)
        if (name === 'quantity') setQuantity(value)     
    }

    return (
        <div>
            <form action="#" onSubmit={handleSubmit}>
                <label htmlFor="name">Name:
                    <input type="text" value={name} name='name' onChange={handleChange}/>
                </label>
                <label htmlFor="quantity" style={{margin:'0 10px'}}>Quantity:
                    <input type="text" value={quantity} name='quantity' onChange={handleChange}/>
                </label>
                <button type='submit'>Add</button>
            </form>

            <h1>Shopping List</h1>
            <ul>
                {
                    shoppingList.map((item, index) => {
                        return <li key={index}>Name: {item.name} Quantity:{item.quantity}</li>
                    })
                }
            </ul>
        </div>
    )
}

export default ShoppingList

企业微信截图_20250825164411

计算器案例

import { useState } from 'react'
import '../style.css'

const Calculate = () => {
    const [value, setValue] = useState('')
    const calculate = () => {
        const ans = eval(value)
        setValue(ans)
    }
    return (
        <div className='container'>
            <div className="show">
                <p>{value}</p>
            </div>
            <div className="calConatiner">
                <p className='clear' onClick={() => setValue('')}>c</p>
                <p className='btn' onClick={() => setValue(value + '/')}>/</p>
                <p className='btn' onClick={() => setValue(value + '*')}>*</p>
                <p className='btn' onClick={() => setValue(value + '7')}>7</p>
                <p className='btn' onClick={() => setValue(value + '8')}>8</p>
                <p className='btn' onClick={() => setValue(value + '9')}>9</p>
                <p className='btn' onClick={() => setValue(value + '-')}>-</p>
                <p className='btn' onClick={() => setValue(value + '4')}>4</p>
                <p className='btn' onClick={() => setValue(value + '5')}>5</p>
                <p className='btn' onClick={() => setValue(value + '6')}>6</p>
                <p className='plus' onClick={() => setValue(value + '+')}>+</p>
                <p className='btnPosition1' onClick={() => setValue(value + '1')}>1</p>
                <p className='btnPosition2' onClick={() => setValue(value + '2')}>2</p>
                <p className='btnPosition3' onClick={() => setValue(value + '3')}>3</p>
                <p className='btn' onClick={() => setValue(value + '0')}>0</p>
                <p className='btn' onClick={() => setValue(value + '00')}>00</p>
                <p className='btn' onClick={() => setValue(value + '.')}>.</p>
                <p className='btn' onClick={calculate}>=</p>
            </div>
        </div>
    )
}

export default Calculate

body{
    margin: 0;
    padding: 0;
    width: 100vw;
    height: 100vh;
}

#root{
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
}

.container{
    width: 320px;
    height: 550px;
    background-color: #fff;
    border: 1px solid black;
    font-size: 25px;
    text-align: center;
    line-height: 80px;
}

.show{
    height: 150px;
    display: flex;
    align-items: center;
    justify-content: flex-end;
}

.calConatiner{
    height: 400px;
    display: flex;
    flex-wrap: wrap;
    cursor: pointer;
}

.clear{
    width: 160px;
    height: 80px;
    background-color: rgb(250, 162, 0);
    margin: 0;
    color: #fff;
}

.btn{
    width: 80px;
    height: 80px;
    background-color: rgb(41, 41, 41);
    color: #fff;
    margin: 0;
}

.plus{
    width: 80px;
    height: 160px;
    margin: 0;
    background-color: rgb(41, 41, 41);
    color: #fff;
    line-height: 160px;
}

.btnPosition1{
    position: absolute;
    bottom: 132px;
    width: 80px;
    height: 80px;
    background-color: rgb(41, 41, 41);
    color: #fff;
    margin: 0;
}

.btnPosition2{
    position: absolute;
    bottom: 132px;
    left: 332px;
    width: 80px;
    height: 80px;
    background-color: rgb(41, 41, 41);
    color: #fff;
    margin: 0;
}

.btnPosition3{
    position: absolute;
    bottom: 132px;
    left: 412px;
    width: 80px;
    height: 80px;
    background-color: rgb(41, 41, 41);
    color: #fff;
    margin: 0;
}

.btn:hover, 
.plus:hover,
.btnPosition1:hover,
.btnPosition2:hover
.btnPosition3:hover{
    background-color: rgb(250, 162, 0);
}

企业微信截图_20250827103440


购物车案例

语法糖:如果在 JSX 中直接放置一个数组时,React 会自动遍历这个数组,并将数组中的每个元素依次渲染到页面上

例:

父组件:

const Products = ({result}) => {
  return (
    <section className='product'>
      {result}
    </section>
  )
}

export default Products

子组件:直接解构,无需再次遍历循环

export default function card({ img, title, reviews, prePrice, newPrice }) {
  return (
    <div>
      <img src={img} alt={title} />
      <p>{title}</p>
      <span>{reviews}</span>
      <p>{prePrice}</p>
      <p>{newPrice}</p>
    </div >
  )
}

目录结构:
企业微信截图_20250828143752


展示:

企业微信截图_20250828144645

企业微信截图_20250828144700

企业微信截图_20250828144712


App.jsx

import { useState } from 'react'
import Navigation from './Navigation/Nav'
import Products from './Products/Products'
import Reconmended from './Reconmended/Reconmended'
import SideBar from './SideBar/SideBar'
import './index.css'
import products from './db/data'
import Card from './components/Card.jsx'

const App = () => {
  const [selectedCate, setSelectedCate] = useState(null)
  const [query, setQuery] = useState('')

  const handleSearch = (e) => setQuery(e.target.value)

  const filterItem = products.filter(product => product.title.toLocaleLowerCase().indexOf(query.toLocaleLowerCase()) !== -1)

  const handleRadio = (e) => setSelectedCate(e.target.value)

  const filterData = (products, selected, query) => {
    let filterPro = products

    if (query) filterPro = filterItem
    if (selected && selected !== 'all') {
      // 保留匹配上的
      filterPro = filterPro.filter(({ category, color, company, newPrice, title }) =>
        category === selected || color === selected || company === selected || newPrice === selected || title === selected
      )
    }

    return filterPro.map(({ img, title, reviews, prevPrice, newPrice }) => (
      <Card key={Math.random()} img={img} title={title} review={reviews} prevPrice={prevPrice} newPrice={newPrice}></Card>
    ))
  }

  const result = filterData(products, selectedCate, query)

  return <div className='demo'>
    <SideBar handleRadio={handleRadio} />
    <div className="right">
      <Navigation handleSearch={handleSearch} query={query}/>
      <Reconmended handleRadio={handleRadio} />
      <Products result={result}/>
    </div>
  </div>
}

export default App

index.css

*{
    margin: 0;
    padding: 0;
}

.demo{
    display: flex;
}

.right{
    flex: 1;
}

db/data.js

import { AiFillStar } from "react-icons/ai";

const data = [
  {
    img: "https://m.media-amazon.com/images/I/6125yAfsJKL._AC_UX575_.jpg",
    title: "Nike Air Monarch IV",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "200",
    company: "Nike",
    color: "white",
    category: "sneakers",
  },
  {
    img: "https://m.media-amazon.com/images/I/51+P9uAvb1L._AC_UY695_.jpg",
    title: "Nike Waffle One Sneaker",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "200",
    company: "Nike",
    color: "green",
    category: "sneakers",
  },
  {
    img: "https://m.media-amazon.com/images/I/71oEKkghg-L._AC_UX575_.jpg",
    title: "Nike Running Shoe",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "200",
    company: "Adidas",
    color: "black",
    category: "sneakers",
  },
  {
    img: "https://m.media-amazon.com/images/I/41M54ztS6IL._AC_SY625._SX._UX._SY._UY_.jpg",
    title: "Flat Slip On Pumps",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "200",
    company: "Vans",
    color: "green",
    category: "flats",
  },
  {
    img: "https://m.media-amazon.com/images/I/71zKuNICJAL._AC_UX625_.jpg",
    title: "Knit Ballet Flat",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "50",
    company: "Adidas",
    color: "black",
    category: "flats",
  },

  {
    img: "https://m.media-amazon.com/images/I/61V9APfz97L._AC_UY695_.jpg",
    title: "Loafer Flats",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "50",
    company: "Vans",
    color: "white",
    category: "flats",
  },

  {
    img: "https://m.media-amazon.com/images/I/71VaQ+V6XnL._AC_UY695_.jpg",
    title: "Nike Zoom Freak",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "200",
    company: "Nike",
    color: "green",
    category: "sneakers",
  },

  {
    img: "https://m.media-amazon.com/images/I/61-cBsLhJHL._AC_UY695_.jpg",
    title: "Nike Men's Sneaker",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "200",
    company: "Adidas",
    color: "blue",
    category: "sneakers",
  },

  {
    img: "https://m.media-amazon.com/images/I/81xXDjojYKS._AC_UX575_.jpg",
    title: "PUMA BLACK-OCE",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "150",
    company: "Puma",
    color: "green",
    category: "sneakers",
  },
  {
    img: "https://m.media-amazon.com/images/I/71E75yRwCDL._AC_UY575_.jpg",
    title: "Pacer Future Sneaker",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "150",
    company: "Puma",
    color: "red",
    category: "sneakers",
  },
  {
    img: "https://m.media-amazon.com/images/I/71jeoX0rMBL._AC_UX575_.jpg",
    title: "Unisex-Adult Super",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "150",
    company: "Puma",
    color: "black",
    category: "sneakers",
  },
  {
    img: "https://m.media-amazon.com/images/I/61TM6Q9dvxL._AC_UX575_.jpg",
    title: "Roma Basic Sneaker",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "150",
    company: "Puma",
    color: "white",
    category: "sneakers",
  },
  {
    img: "https://m.media-amazon.com/images/I/7128-af7joL._AC_UY575_.jpg",
    title: "Pacer Future Doubleknit",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "150",
    company: "Puma",
    color: "black",
    category: "sneakers",
  },

  {
    img: "https://m.media-amazon.com/images/I/81xXDjojYKS._AC_UX575_.jpg",
    title: "Fusion Evo Golf Shoe",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "100",
    company: "Puma",
    color: "green",
    category: "sneakers",
  },
  {
    img: "https://m.media-amazon.com/images/I/719gdz8lsTS._AC_UX575_.jpg",
    title: "Rainbow Chex Skate",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "100",
    company: "Vans",
    color: "red",
    category: "flats",
  },
  {
    img: "https://m.media-amazon.com/images/I/71gpFHJlnoL._AC_UX575_.jpg",
    title: "Low-Top Trainers",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "100",
    company: "Vans",
    color: "white",
    category: "sandals",
  },
  {
    img: "https://m.media-amazon.com/images/I/71pf7VFs9CL._AC_UX575_.jpg",
    title: "Vans Unisex Low-Top",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "100",
    company: "Vans",
    color: "blue",
    category: "sandals",
  },
  {
    img: "https://m.media-amazon.com/images/I/61N4GyWcHPL._AC_UY575_.jpg",
    title: "Classic Bandana Sneakers",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "50",
    company: "Nike",
    color: "black",
    category: "sandals",
  },
  {
    img: "https://m.media-amazon.com/images/I/61bncQ44yML._AC_UX695_.jpg",
    title: "Chunky High Heel",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "50",
    company: "Vans",
    color: "black",
    category: "heels",
  },
  {
    img: "https://m.media-amazon.com/images/I/71czu7WgGuL._AC_UY695_.jpg",
    title: "Slip On Stiletto High Heel",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "100",
    company: "puma",
    color: "black",
    category: "heels",
  },
  {
    img: "https://m.media-amazon.com/images/I/61men05KRxL._AC_UY625_.jpg",
    title: "DREAM PAIRS Court Shoes",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "150",
    company: "Nike",
    color: "red",
    category: "heels",
  },
  {
    img: "https://m.media-amazon.com/images/I/519MRhRKGFL._AC_UX575_.jpg",
    title: "Nike Air Vapormax Plus",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "200",
    company: "Nike",
    color: "red",
    category: "sneakers",
  },

  {
    img: "https://m.media-amazon.com/images/I/51PGWTXgf-L._AC_UY625_.jpg",
    title: "Low Mid Block Heels",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "200",
    company: "Nike",
    color: "black",
    category: "heels",
  },
  {
    img: "https://m.media-amazon.com/images/I/616sA5XUKtL._AC_UY675_.jpg",
    title: "Chunky High Heel",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "50",
    company: "Adidas",
    color: "black",
    category: "heels",
  },
  {
    img: "https://m.media-amazon.com/images/I/71h5+MbEK7L._AC_UY625_.jpg",
    title: "Amore Fashion Stilettos",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "150",
    company: "Adidas",
    color: "white",
    category: "heels",
  },
  {
    img: "https://m.media-amazon.com/images/I/61uw5RDxKQL._AC_UY625_.jpg",
    title: "Bridal Sandals Glitter",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "50",
    company: "Adidas",
    color: "black",
    category: "heels",
  },
  {
    img: "https://m.media-amazon.com/images/I/71yhoZP0l6L._AC_UY695_.jpg",
    title: "Wedding Prom Bridal",
    star: '<AiFillStar className="rating-star" />',
    reviews: "(123 reviews)",
    prevPrice: "$140,00",
    newPrice: "50",
    company: "Adidas",
    color: "black",
    category: "flats",
  },
];

export default data;

Navigation/Nav.jsx

import './Nav.css'
import { IoMdHeartEmpty } from "react-icons/io";
import { IoCartOutline } from "react-icons/io5";
import { FiUser } from "react-icons/fi";

const Nav = ({handleSearch}) => {
  return (
    <nav className='navContainer'>
      <div className="search">
        <input type="text" className='searchInput' placeholder='Enter your search shoes.' onChange={handleSearch}/>
      </div>

      <div className="profileContainer">
          <IoMdHeartEmpty  className='navIcon'/>
          <IoCartOutline className='navIcon'/>
          <FiUser className='navIcon'/>
      </div>
    </nav>
  )
}

export default Nav

Navigation/Nav.css

*{
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

.navContainer{
    display: flex;
    align-items: center;
    padding: 0 100px;
    border-bottom: 1px solid #f3f3f3;
    height: 50px;
    width: 100%;
    justify-content: space-between;
}

.searchInput{
    outline: none;
    border: none;
    background-color: #f7f6f6;
    padding: 7px 10px;
    border-radius: 5px;
}

.navIcon{
    color: rgb(41, 41, 41);
    font-size: 18px;
    margin-right: 20px;
}

Products/Products.jsx

import './Products.css'
import Card from '../components/Card'

const Products = ({result}) => {
  return (
    <section className='product'>
      {result}
    </section>
  )
}

export default Products

Products/Products.css

*{
    margin: 0;
    padding: 0;
}

.cardContainer{
    margin-top: 50px;
    margin-left: 50px;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    
}

.product{
    display: flex;
    justify-content: flex-start;
    flex-wrap: wrap;
}

.card{
    width: 200px;
    height: 175px;
    border: 1px solid #ededed;
    padding: 10px;
    cursor: pointer;
}

.pic{
    display: flex;
    justify-content: center;
}

img{
    width: 120px;
    height: 60px;
}

.proTitle{
    padding-top: 10px;
    font-weight: 700;
    font-size: 14px;
    height: 40px;
}

.review{
    white-space: nowrap;
    font-size: 12px;
    margin-left: 5px;
}

.starContainer{
    display: flex;
    align-items: center;
    margin: 8px 0;
}

.star{
    color: #d5ab55;
}

.price{
    display: flex;
    font-size: 13px;
}

.oldPrice{
    font-size: 13px;
    margin-right: 5px;
    text-decoration: line-through;
}

.lock{
    font-size: 13px;
}

.price_lock{
    display: flex;
    justify-content: space-between;
    align-items: center;
}

Recommended/Recommended.jsx

import './Reconmended.css'

const Reconmended = ({handleRadio}) => {
  return (
    <section className='recommendedContainer'>
      <p className='title'>Recommended</p>

      <div className="products">
        <button className="item" value='all' onClick={handleRadio}>All Products</button>
        <button className="item" value='Nike' onClick={handleRadio}>Nike</button>
        <button className="item" value='Adidas' onClick={handleRadio}>Adidas</button>
        <button className="item" value='Puma' onClick={handleRadio}>Puma</button>
        <button className="item" value='Vans' onClick={handleRadio}>Vans</button>
      </div>
    </section>
  )
}

export default Reconmended

Recommended/Recommended.css

*{
    margin: 0;
    padding: 0;
}

.recommendedContainer{
    margin-top: 20px;
    margin-left: 50px;
}

.title{
    font-weight: 700;
    margin-bottom: 20px;
}

.item{
    color: rgb(134, 131, 131);
    font-size: 13px;
    border: 1px solid rgb(134, 131, 131);
    padding: 5px 10px;
    border-radius: 5px;
    margin-right: 10px;
    cursor: pointer;
    background-color: #fff;
}

Sidebar/Category/Category.jsx

import './Category.css'
import Radio from '../../components/Radio'

const Category = ({ handleRadio }) => {
  return (
    <div className="categoryContainer">
      <p className='sidebarTitle'>Category</p>
      <div className='select'>        
        <Radio  handleRadio={handleRadio} value='all' title='All' name='type'  classStyle='radio'/>
        <Radio  handleRadio={handleRadio} value='sneakers'  title='Skneakers' name='type'   classStyle='radio'/>
        <Radio  handleRadio={handleRadio} value='flats'  title='Flats' name='type'   classStyle='radio'/>
        <Radio  handleRadio={handleRadio} value='heels'  title='Heels' name='type'   classStyle='radio'/>
        <Radio  handleRadio={handleRadio} value='sandals'  title='Sandals' name='type'   classStyle='radio'/>
      </div>
    </div>
  )
}

export default Category

SideBar/Category/Category.css

*{
    margin: 0;
    padding: 0;
}

.categoryContainer{
    display: flex;
    /* justify-content: center; */
    flex-wrap: wrap;
}

.select{
    margin-left: 82px;
}

.sidebarTitle{
    width: 100%;
    text-align: center;
    margin: 20px 0;
}

.radio{
    margin-right: 10px;
    outline: none;
    cursor: pointer;
}

.cateTitle{
    font-size: 14px;
    color: rgb(41, 41, 41);
}

SideBar/Colors/Colors.jsx

import './Color.css'
import Radio from '../../components/Radio'

const Colors = ({handleRadio}) => {
  return (
    <div className='categoryContainer'>
      <p className='sidebarTitle'>Colors</p>
      <div className='select'>
        <Radio  handleRadio={handleRadio} value='all' title='All' name='color' classStyle='all' />
        <Radio  handleRadio={handleRadio} value='black'  title='Black' name='color' classStyle='black' />
        <Radio  handleRadio={handleRadio} value='blue'  title='Blue' name='color' classStyle='blue' />
        <Radio  handleRadio={handleRadio} value='red'  title='Red' name='color' classStyle='red' />
        <Radio  handleRadio={handleRadio} value='green'  title='Green' name='color' classStyle='green' />
        <Radio  handleRadio={handleRadio} value='white'  title='White' name='color' classStyle='white' />
      </div>
    </div>
  )
}

export default Colors

SideBar/Colors/Colors.css

.all,.black,.blue,.red,.green,.white{
    margin-right: 10px;
    outline: none;
    position: relative;
    cursor: pointer;
}

.all::after,.black::after,.blue::after,.red::after,.green::after,.white::after{
    position: absolute;
    content: '';
    width: 13px;
    height: 13px;
    border-radius: 50%;
    background-color: red;
    z-index: 999;
    top: 0;
    left: 0;
}

.all::after{
    background: linear-gradient(blue, crimson)
}



.black::after{
    background-color: black;
}

.blue::after{
    background-color: blue;
}

.green::after{
    background-color: green;
}

.white::after{
    background-color: #fff;
}

input[type="radio"]:checked {
    transform: scale(1.2);
}

SideBar/Price/Price.jsx

import './Price.css'
import Radio from '../../components/Radio'

const Price = ({handleRadio}) => {
  return (
    <div className="categoryContainer">
      <p className='sidebarTitle'>Price</p>
      <div className='select'>
        <Radio  handleRadio={handleRadio} value='all' title='All' name='price' classStyle='radio' />
        <Radio  handleRadio={handleRadio} value='50'  title='0-$50' name='price' classStyle='radio' />
        <Radio  handleRadio={handleRadio} value='100'  title='$50-$100' name='price' classStyle='radio' />
        <Radio  handleRadio={handleRadio} value='150'  title='$100-$150' name='price' classStyle='radio' />
        <Radio  handleRadio={handleRadio} value='200'  title='over $150' name='price' classStyle='radio' />
      </div>

    </div>
  )
}

export default Price

SideBar/SideBar.jsx

import { GiShoppingCart } from "react-icons/gi";
import './SideBar.css'
import Category from './Category/Category'
import Price from './Price/Price'
import Colors from './Colors/Colors'

const SideBar = ({handleRadio}) => {
  return (
    <div className="sideBar">
        <div className="logo">
            <GiShoppingCart className="shoppingCart"/>
        </div>

        <Category handleRadio={handleRadio}/>
        <Price handleRadio={handleRadio}/>
        <Colors handleRadio={handleRadio}/>
    </div> 
  )
}

export default SideBar

SideBar/SideBar.css

*{
    margin: 0;
    padding: 0;
}

.sideBar{
    width: 250px;
    height: 100vh;
    border-right: 1px solid #f3f3f3;
}

.logo{
    text-align: center;
    border-bottom: 1px solid #f3f3f3;
    height: 50px;
    padding: 10px 0;
}

.shoppingCart{
    font-size: 30px;
}

Components/Card.jsx

import { FaStar } from "react-icons/fa"
import { IoBag } from "react-icons/io5";

export default function card({ img, title, reviews, prevPrice, newPrice }) {
  return (
    <div div className='cardContainer'>
      <div className="card">
        <div className="pic">
          <img src={img} alt={title} />
        </div>
        <div className="detail">
          <p className='proTitle'>{title}</p>
          <div className='starContainer'>
            <FaStar className='star' /><FaStar className='star' /><FaStar className='star' /><FaStar className='star' />
            <span className='review'>{reviews}</span>
          </div>
          <div className='price_lock'>
            <div className="price">
              <p className='oldPrice'>{prevPrice}</p>
              <p className='newPrice'>{newPrice}</p>
            </div>
            <IoBag className='lock' />
          </div>
        </div>
      </div>
    </div >
  )
}

Components/Radio.jsx

// 代码优化

const Radio = ({handleRadio, value, title, name, classStyle}) => {
  return (
    <div>
        <input type="radio" name={name} className={classStyle} onChange={handleRadio} value={value}/>
        <span className='cateTitle'>{title}</span>
    </div>
  )
}

export default Radio
posted @ 2025-08-21 16:36  原语  阅读(19)  评论(0)    收藏  举报