跳到内容

e

给React应用加点样式

我们目前的应用的外观是相当简陋的。在练习0.2中,任务是通过Mozilla的CSS教程

在我们进入下一部分之前,让我们看一下如何在React应用中添加样式。有几种不同的方法,我们将在后面看一下其他的方法。首先,我们将以老式的方式向我们的应用添加CSS;在一个文件中,不使用CSS预处理器(尽管这并不完全正确,我们将在后面学习)。

让我们在src目录下添加一个新的index.css文件,然后通过在index.js文件中导入它来添加到应用。

import './index.css'

让我们在index.css文件中添加以下CSS规则。

h1 {
  color: green;
}

注意:当文件index.css的内容发生变化时,React可能不会自动注意到,所以你可能需要刷新浏览器才能看到你的变化!

CSS规则由选择器声明组成。选择器定义了该规则应该应用于哪些元素。上面的选择器是h1,它将匹配我们应用中所有的h1头标签。

该声明将color属性设置为green值。

一条CSS规则可以包含任意数量的属性。让我们修改前面的规则,通过定义字体样式为italic,使文字变成草书。

h1 {
  color: green;
  font-style: italic;}

通过使用不同类型的CSS选择器,有许多匹配元素的方法。

如果我们想用我们的样式针对,比方说,每一个笔记,我们可以使用选择器li,因为所有的笔记都被包裹在li标签里。

const Note = ({ note, toggleImportance }) => {
  const label = note.important
    ? 'make not important'
    : 'make important';

  return (
    <li>
      {note.content}
      <button onClick={toggleImportance}>{label}</button>
    </li>
  )
}

让我们在我们的样式表中加入以下规则(因为我对优雅的网页设计的知识接近于零,所以这些样式并没有什么意义)。

li {
  color: grey;
  padding-top: 3px;
  font-size: 15px;
}

使用元素类型来定义CSS规则是有点问题的。如果我们的应用包含其他的li标签,同样的样式规则也会应用于它们。

如果我们想把我们的样式专门应用于笔记,那么最好使用类选择器

在常规HTML中,类被定义为class属性的值。

<li class="note">some text...</li>

在React中,我们必须使用className属性而不是class属性。考虑到这一点,让我们对我们的Note组件做如下修改。

const Note = ({ note, toggleImportance }) => {
  const label = note.important
    ? 'make not important'
    : 'make important';

  return (
    <li className='note'>      {note.content}
      <button onClick={toggleImportance}>{label}</button>
    </li>
  )
}

类选择器是用.classname语法定义的。

.note {
  color: grey;
  padding-top: 5px;
  font-size: 15px;
}

如果你现在在应用中添加其他li元素,它们将不会受到上述样式规则的影响。

Improved error message

我们之前实现了当用户试图用alert方法切换已删除笔记的重要性时显示的错误信息。让我们把这个错误信息实现为它自己的React组件。

这个组件很简单。

const Notification = ({ message }) => {
  if (message === null) {
    return null
  }

  return (
    <div className='error'>
      {message}
    </div>
  )
}

如果messageprop的值是null,那么就不会有任何东西渲染在屏幕上,而在其他情况下,信息会渲染在一个div元素中。

让我们在App组件中添加一个新的状态,叫做errorMessage。让我们用一些错误信息来初始化它,这样我们就可以立即测试我们的组件。

const App = () => {
  const [notes, setNotes] = useState([])
  const [newNote, setNewNote] = useState('')
  const [showAll, setShowAll] = useState(true)
  const [errorMessage, setErrorMessage] = useState('some error happened...')
  // ...

  return (
    <div>
      <h1>Notes</h1>
      <Notification message={errorMessage} />      <div>
        <button onClick={() => setShowAll(!showAll)}>
          show {showAll ? 'important' : 'all' }
        </button>
      </div>
      // ...
    </div>
  )
}

然后让我们添加一个适合错误信息的样式规则。

.error {
  color: red;
  background: lightgrey;
  font-size: 20px;
  border-style: solid;
  border-radius: 5px;
  padding: 10px;
  margin-bottom: 10px;
}

现在我们准备添加显示错误信息的逻辑。让我们以下列方式改变toggleImportanceOf函数。

  const toggleImportanceOf = id => {
    const note = notes.find(n => n.id === id)
    const changedNote = { ...note, important: !note.important }

    noteService
      .update(changedNote).then(returnedNote => {
        setNotes(notes.map(note => note.id !== id ? note : returnedNote))
      })
      .catch(error => {
        setErrorMessage(          `Note '${note.content}' was already removed from server`        )        setTimeout(() => {          setErrorMessage(null)        }, 5000)        setNotes(notes.filter(n => n.id !== id))
      })
  }

当错误发生时,我们在errorMessage状态中添加一个描述性的错误信息。同时,我们启动一个定时器,在五秒后将errorMessage状态设置为null

结果看起来是这样的。

fullstack content

我们应用当前状态的代码可以在GitHub上的part2-7分支找到。

Inline styles

React也可以在代码中直接编写样式,即所谓的内联样式

定义内联样式的想法非常简单。任何React组件或元素都可以通过style属性作为JavaScript对象提供一组CSS属性。

CSS规则在JavaScript中的定义与普通的CSS文件略有不同。比方说,我们想给某个元素加上绿色和16像素的斜体字。在CSS中,它看起来是这样的。

{
  color: green;
  font-style: italic;
  font-size: 16px;
}

但是作为React的内联样式对象,它会如下所示:

{
  color: 'green',
  fontStyle: 'italic',
  fontSize: 16
}

每个CSS属性都被定义为JavaScript对象的一个独立属性。像素的数字值可以简单地定义为整数。与普通的CSS相比,其中一个主要区别是,连字符(kebab case)的CSS属性是用camelCase写的。

接下来,我们可以通过创建一个Footer组件并为其定义以下内联样式,为我们的应用添加一个 "底层块"。

const Footer = () => {  const footerStyle = {    color: 'green',    fontStyle: 'italic',    fontSize: 16  }  return (    <div style={footerStyle}>      <br />      <em>Note app, Department of Computer Science, University of Helsinki 2022</em>    </div>  )}
const App = () => {
  // ...

  return (
    <div>
      <h1>Notes</h1>

      <Notification message={errorMessage} />

      // ...

      <Footer />    </div>
  )
}

内联样式有某些限制。例如,所谓的伪类不能被直接使用。

内联样式和其他一些为React组件添加样式的方式完全违背了旧的惯例。传统上,人们认为最好的做法是将CSS与内容(HTML)和功能(JavaScript)完全分开。根据这个老的思想流派,目标是将CSS、HTML和JavaScript写进各自的文件。

事实上,React的哲学是与此截然相反的。由于将CSS、HTML和JavaScript分离到不同的文件中,在大型应用中似乎不能很好地扩展,React将应用的划分建立在其逻辑功能实体的基础上。

构成应用功能实体的结构单元是React组件。一个React组件定义了构造内容的HTML,决定功能的JavaScript函数,以及组件的样式;所有这些都在一个地方。这是为了创建尽可能独立和可重复使用的单个组件。

我们应用的最终版本的代码可以在GitHubpart2-8分支中找到。