从0学习react第一篇, jsx基础

69 阅读5分钟

jsx是什么

function App() {
  return (
    <div>
      你好,世界
    </div>
  );
}

export default App;

可以看到,jsx是一种基于JavaScript和xml编写的,具有这两种语言优点的一种编写UI的新方式,是js的语法扩展

通过webpack的babel编译jsx,将其转换为虚拟dom和渲染函数,通过js渲染原生dom和页面

jsx的特点

  • 具有js的可编程能力
  • 具有xml的声明式语法

jsx的常见应用

js表达式基础

在jsx模板中,使用{}来声明表达式,它其中的内容会隐式转换为字符串,显示在页面上

const obj = {
    str: '字符串',
    num: 1,
    fn() {return '方法'}
}

function App() {
  return (
    // 这里的样式是直接在{}里插入对象
    <div style={{color: 'red'}}>
      {obj.str} {obj.num} {obj.fn()}
    </div>
  );
}

export default App;

image.png

循环渲染

循环渲染,用于结构相似的元素重复渲染,jsx的循环渲染,就是函数重复返回xml,只能说熟悉的同时又有些陌生

循环时使用key属性提高性能,key属性的值必须在循环中是唯一的,通常使用id

const list = ['Java', 'C', 'Rust']

function App() {
  return (
    <ul>
        {/* 可以看到,表达式中的函数仍可以写xml语句,非常灵活 */}
        { list.map(item => <li key={item}>{item}</li>) }
    </ul>
  );
}

export default App;

条件渲染

条件渲染用于动态的控制页面元素的显示隐藏

简单条件渲染

仅仅只有一个条件,控制页面元素的显示隐藏状态

const userInfo = {
    name: '张三',
    age: 12
}

let isEdit = false
let isShow = true

function App() {
  return (
    <div>
        {/* 通过 三元表达式 或 短路与 来控制显示隐藏 */}
        {isEdit ? <input /> : <span>{userInfo.name}</span>} <br/>
        {isShow && <span>{userInfo.age}</span>}
    </div>
  );
}

export default App;

复杂条件渲染

其实大家应该都有想法了,就是通过函数的if或者switch做判断,返回不同的xml语句即可

let key = 'b'

const fn = (key) => {
    switch(key) {
        case 0 : 
        return <span>key为0</span>
        
        case 1 : 
        return <span>key为1</span>
        
        default :
        return typeof key === 'string' ? <div>key为字符串'{key}'</div> : <p>默认</p>
    }
}
function App() {
  return (
    <div>
        { fn(key) }
    </div>
  );
}

export default App;

绑定事件

在js中给dom绑定事件,需要先获取dom元素,比较麻烦,如果直接在html标签上以属性的形式来添加事件会方便很多,

而在jsx中,dom实际是通过render函数创建的,它会解析jsx中的xml语句,用于生成dom,那为什么不改造一下渲染函数,使他能实现上面的方便事件绑定功能呢?实际上,jsx已经实现了这个功能

通过添加on + 事件名的属性,即可在html元素上绑定事件,它的值即是需要执行的回调函数

带事件对象的普通事件处理函数

function App() {
    // e是事件对象
    const fn = (e) => {
        console.log('被点击了', e);
    }
    return (
      <div>
          <button onClick={fn}>点我</button>
      </div>
    );
}

export default App;

需要传参的事件处理函数

const name = '张三'

function App() {
    const fn = (name) => {
        console.log('是' + name);
    }
    return (
      <div>
          <button onClick={() => fn(name)}>谁点我?</button>
      </div>
    );
}

export default App;

需要传参的带事件对象的事件处理函数

const name = '张三'

function App() {
    const fn = (e, name) => {
        console.log('是' + name, e);
    }
    return (
      <div>
          <button onClick={(e) => fn(e, name)}>谁点我?</button>
      </div>
    );
}

export default App;

动态数据渲染

如果我们需要实现点击按钮,控制一个div的显示和隐藏,大家会怎么写?

我在下面写了一段代码,他能实现我刚刚所需要的功能吗?

const userInfo = {
    name: '张三'
}

let isShow = true

function App() {
    const fn = () => {
        console.log(isShow ? '显示' : '隐藏');
        isShow = !isShow
    }
    return (
      <div>
          {isShow && <div>{userInfo.name}</div>}
          <button onClick={fn}>点击</button>
      </div>
    );
}

export default App;

很奇怪,我们能看到控制台正确的打印了div的显示隐藏状态,但实际上,他并没有随着数据改变而改变状态

image.png

数据的改变,其实并不会动态的影响到页面的展示,这显然有点反逻辑

而react提供了useState这个API来解决这个问题,它返回一个数组,第一个元素是变量的值,第二个元素是一个方法,能够根据参数修改这个值并在页面中渲染,利用解构,我们可以这么写:

const [变量名, 修改变量值并渲染的方法] = useState(初始值)

注意:

  1. 必须用useState提供的方法修改值,否则无法动态渲染
  2. 复杂数据类型赋新值的时候,需要创建一个新的引用,也就是说如果你的对象中某个属性变了,你需要新创建一个对象作为set函数的实参

简单数据类型

import { useState } from "react";

const userInfo = {
    name: '张三'
}

function App() {
    // 使用useState来给xml元素绑定状态(数据)
    // 通过解构,声明isShow存储值,声明setIsShow存储修改isShow的值的方法,
    // useState的实参,是声明的变量的默认值
    const [isShow, setIsShow] = useState(true)

    const fn = () => {
        setIsShow(!isShow)
        console.log(isShow ? '显示' : '隐藏');
    }
    return (
      <div>
          {isShow && <div>{userInfo.name}</div>}
          <button onClick={fn}>点击</button>
      </div>
    );
}

export default App;

复杂(引用)数据类型

import { useState } from "react";

function App() {
    const [userInfo, setUserInfo] = useState({
        name: '张三'
    })

    const fn = () => {
        // 利用解构和它的覆盖,修改属性的值,添加同理,删除的话先删在解构即可
        setUserInfo({
            ...userInfo,
            name: '李四'
        })
        // 或者使用传入函数的方式
        // setUserInfo((userInfo) => {
        //     userInfo.name = '李四'
        //     return {...userInfo}
        // })
    }
    return (
      <div>
          <div>{userInfo.name}</div>
          <button onClick={fn}>点击将用户名称改为李四</button>
      </div>
    );
}

export default App;

很好,这下功能实现了...吗?

image.png

很喜欢程序员发现bug时的一句话,啊?

如果不是我在这里打印,我还真没发现,聪明的小伙伴估计已经猜到了,这个值的修改,是异步的,我们没法修改之后直接拿过来用!

那么解决方法是什么呢?

很简单,搞一个新变量存一下新值,之后使用这个变量即可,也可以使用useEffect