细节

  • 输入造成的错误。

    当想把return 的 GuestGreeting 和 UserGreeting 互换的时候,手动输入两个函数而不是复制粘贴。手动输入把Guest改为User,User改为Guest,当第一个改完,第二个没改完的时候,第二个函数是 xxGreeting,工具会认为是Greeting,因为 Function函数定义的是 Greeting,自己嵌套调用自己会造成死循环。

    1. function Greeting(props) {
    2. const isLoggedIn = props.isLoggedIn;
    3. if (isLoggedIn) {
    4. return <GuestGreeting />;
    5. }
    6. return <UserGreeting />;
    7. }
  • 整体思路 1定义了函数的方法和 h 和 button

    2 定义了一些类和渲染render
    3 执行class 类的结构体contract,showwarning=true
    正确的时候,render三目运算符执行的是Hide,由于三目运算符里面嵌套了showwarning,showarning返回上面的 warningBanner.
    4 warningBanner执行function 的if ,由于点击以后 Hide变成了 show,这时候 props warn变成了 ! 点击之前 是 true

  1. function WarningBanner(props) {if (!props.warn) {return null;}
  2. return (<div className="warning">Warning!</div>);}
  3. class Page extends React.Component {
  4. constructor(props) {super(props);this.state = {showWarning: true}
  5. //如果改为false,会先展示showthis.handleToggleClick = this.handleToggleClick.bind(this);
  6. }handleToggleClick() {this.setState(prevState => ({
  7. showWarning: !prevState.showWarning}));}
  8. render() {return (<div><WarningBanner warn={this.state.showWarning} />
  9. <button onClick={this.handleToggleClick}>{this.state.showWarning ? 'Hide' : 'Show'}
  10. </button></div>
  11. // 如果是 ?show:hide 会先显示 show 和 warning);}}
  12. ReactDOM.render(<Page />,document.getElementById('root'));
  1. body {background: #BBFFBB;}
  2. * {font-family: sans-serif;
  3. margin: 0;}
  4. button {height: 40px;
  5. width: 200px;}
  6. .warning {background-color: red;
  7. text-align: center;
  8. width: 100%;
  9. padding: 10px;
  10. font-size: 14pt;
  11. color: white;}
  • 钥匙帮助反应识别哪些项目已经改变,添加或删除。钥匙要给数组元素内的元素一个稳定的身份:
    1. const numbers = [1, 2, 3, 4, 5];
    2. const listItems = numbers.map((number) =><li key={number.toString()}>
    3. {number}</li>);

选择一个最好方法主要是使用一个字符串,唯一地标识一个列表项在其兄弟姐妹。从你的数据通常可以使用id作为键

  1. const todoItems = todos.map((todo) =><li key={todo.id}>{todo.text}</li>);

当你没有稳定的IDs呈现物品,你可以使用项指数作为一个关键作为最后的手段

  1. const todoItems = todos.map((todo, index) =>
  2. // Only do this if items have no stable IDs
  3. <li key={index}>
  4. {todo.text}
  5. </li>
  6. );
  • numberOfGuests 方法。//如果把初始值2改成undefined 会有一个默认值。本方法默认值是0

    undefined的时候会有一个“警告:一个组件正在改变一个不受控制的输入类型% s的控制。输入元素不应该从控制转向控制(反之亦然)。使用受控或不受控制的输入元素之间决定组件的生命周期。更多信息:https://fb。我/ react-controlled-components % s”“数”“

    1. class Reservation extends React.Component {
    2. constructor(props){super(props);
    3. this.state = {isGoing: true,numberOfGuests: 2};
    4. //如果把2改成undefined 会有一个默认值。本方法默认值是0,这时候出现“警告:
    5. //一个组件正在改变一个不受控制的输入类型% s的控制。输入元素不应该从控制转向控制(反之亦然)。
    6. //使用受控或不受控制的输入元素之间决定组件的生命周期。
    7. this.handleInputChange = this.handleInputChange.bind(this);}
    8. handleInputChange(event) {const target = event.target;
    9. const value = target.type === 'checkbox' ? target.checked : target.value;
    10. const name = target.name;
    11. this.setState({[name]: value});}
    12. render() {
    13. return (<form><label>Is going:<input name="isGoing"type="checkbox"
    14. checked={this.state.isGoing} onChange={this.handleInputChange} /></label>
    15. <br /><label>Number of guests:<input name="numberOfGuests"
    16. type="number" value={this.state.numberOfGuests}
    17. onChange={this.handleInputChange} /></label></form>);}}
    18. ReactDOM.render(<Reservation />,document.getElementById('root'));

编辑的时候发生的事情:
1、react调用在dom上指定为onchange的函数。在我们的例子中,这是温度输入组件中的handlechange方法。
2、temperatureInput组件中的handleChange方法使用新的所需值调用此.props.ontemperatureChange()。它的道具,包括温度变化,是由它的父组件计算器提供的。
3、在先前渲染时,计算器指定摄氏温度输入的温度范围是计算器的handlecelsiuschange方法,而华氏温度输入的温度范围是计算器的handle华氏温度范围方法。所以根据我们编辑的输入调用这两个计算器方法中的任何一个。
在这些方法中,计算器组件通过使用新的输入值和刚刚编辑的输入的当前比例调用这个.setState(),要求react重新呈现自己。
4、react调用计算器组件的呈现方法来了解UI的外观。两个输入的值都是根据当前温度和活动刻度重新计算的。这里进行温度转换。
5、react使用计算器指定的新属性调用各个温度输入组件的渲染方法。它了解用户界面应该是什么样子。
react调用boilingjustion组件的render方法,将摄氏温度作为其道具传递。
react dom用沸腾的结论更新dom并匹配所需的输入值。我们刚刚编辑的输入接收其当前值,另一个输入在转换后更新为温度。
每次更新都要经过相同的步骤,以便输入保持同步。

  1. const scaleNames = {c: 'Celsius',f: 'Fahrenheit'};
  2. function toCelsius(fahrenheit) {return (fahrenheit - 32) * 5 / 9;}
  3. function toFahrenheit(celsius) {return (celsius * 9 / 5) + 32;}
  4. function tryConvert(temperature, convert) {const input = parseFloat(temperature);
  5. if (Number.isNaN(input)) {return '';}
  6. const output = convert(input);
  7. const rounded = Math.round(output * 1000) / 1000;
  8. return rounded.toString();}
  9. function BoilingVerdict(props) {if (props.celsius >= 100) {
  10. return <p>The water would boil.</p>;}return <p>The water would not boil.</p>;}
  11. class TemperatureInput extends React.Component {
  12. constructor(props) {
  13. super(props);this.handleChange = this.handleChange.bind(this);}
  14. handleChange(e) {this.props.onTemperatureChange(e.target.value);}
  15. render(){const temperature = this.props.temperature;const scale = this.props.scale;
  16. return(<fieldset><legend>Enter temperature in {scaleNames[scale]}:</legend>
  17. <input value={temperature} onChange={this.handleChange} /></fieldset>);}
  18. }
  19. class Calculator extends React.Component {
  20. constructor(props) {super(props);
  21. this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
  22. this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
  23. this.state = {temperature: '', scale: 'c'};}
  24. handleCelsiusChange(temperature) {this.setState({scale: 'c', temperature});}
  25. handleFahrenheitChange(temperature) {this.setState({scale: 'f', temperature});}
  26. render() {
  27. const scale = this.state.scale;
  28. const temperature = this.state.temperature;
  29. const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
  30. const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;
  31. return(<div><TemperatureInput scale="c" temperature={celsius}
  32. onTemperatureChange={this.handleCelsiusChange} /><TemperatureInput scale="f"
  33. temperature={fahrenheit}onTemperatureChange={this.handleFahrenheitChange} />
  34. <BoilingVerdict celsius={parseFloat(celsius)} /></div>);}
  35. }
  36. ReactDOM.render(<Calculator />, document.getElementById('root'));
  • 经常向用户显示JSON数据模型,因此会发现,如果模型构建正确,那么您的UI(以及组件结构)将很好地映射。这是因为用户界面和数据模型倾向于遵循相同的信息体系结构,这意味着将用户界面分离为组件的工作通常是微不足道的。只需将其分解为只代表数据模型一部分的组件即可。

Composition组合vs Inheritance继承

经常反应的问题,这些问题涉及到继承,并展示如何用组合来解决它们。
比如Containment遏制。有些组件不了解他们的孩子。这对于侧边栏或对话框等表示通用“框”的组件尤其常见。
建议这些组件使用特殊的children属性将children元素直接传递到其输出中:这允许其他组件通过嵌套JSX将任意子级传递给它们:

  1. function FancyBorder(props) {
  2. return (<div className={'FancyBorder FancyBorder-' + props.color}>
  3. {props.children}</div>);}
  4. function WelcomeDialog() {
  5. return (<FancyBorder color="blue"><h1 className="Dialog-title">
  6. Welcome</h1><p className="Dialog-message">Thank you for visiting our spacecraft!
  7. </p></FancyBorder>);}
  8. ReactDOM.render(<WelcomeDialog />,document.getElementById('root'));
  1. .FancyBorder {padding: 10px 10px;
  2. border: 10px solid;}
  3. .FancyBorder-blue {border-color: blue;}
  4. .Dialog-title {margin: 0;font-family: sans-serif;}
  5. .Dialog-message {font-size: larger;}

jsx标记中的任何内容都作为子属性传递到fancyborder组件中。由于fancyborder在

中呈现props.children,传递的元素将显示在最终输出中。
虽然这不太常见,但有时可能需要在一个组件中有多个“孔”。在这种情况下,可以提出自己的惯例,而不是使用children:

  1. function Contacts() {return <div className="Contacts" />;}
  2. function Chat() {return <div className="Chat" />;}
  3. function SplitPane(props) {
  4. return (<div className="SplitPane"><div className="SplitPane-left">{props.left}</div>
  5. <div className="SplitPane-right">{props.right}</div></div>);}
  6. function App() {
  7. return (<SplitPane left={<Contacts />}right={ <Chat />} />);}
  8. ReactDOM.render(<App />,document.getElementById('root'));
  1. html, body, #root {width: 100%;
  2. height: 100%;}
  3. .SplitPane {width: 100%;
  4. height: 100%;}
  5. .SplitPane-left {float: left;
  6. width: 30%;
  7. height: 100%;}
  8. .SplitPane-right {float: left;
  9. width: 70%;
  10. height: 100%;}
  11. .Contacts {width: 100%;
  12. height: 100%;
  13. background: lightblue;}
  14. .Chat {width: 100%;
  15. height: 100%;
  16. background: pink;}

这样的反应元素只是对象,所以您可以像其他数据一样将它们作为道具传递。这种方法可能会让您想起其他库中的“插槽”,但是对于作为反应中的道具可以传递的内容没有限制。

Specialization专业化
有时我们认为组件是其他组件的“特殊情况”。例如,我们可以说一个世界喜剧演员是一个特殊的对话案例。
在react中,这也是通过组合实现的,其中一个更“特定”的组件呈现出一个更“通用”的组件,并用props配置它:

  1. function FancyBorder(props) {
  2. return (<div className={'FancyBorder FancyBorder-' + props.color}>
  3. {props.children}</div>);}
  4. function Dialog(props) {
  5. return (<FancyBorder color="blue"><h1 className="Dialog-title">{props.title}</h1>
  6. <p className="Dialog-message">{props.message}</p></FancyBorder>);}
  7. function WelcomeDialog() {
  8. return(<Dialog title="Welcome" message="Thank you for visiting our spacecraft!" />);}
  9. ReactDOM.render(<WelcomeDialog />,document.getElementById('root'));
  1. .FancyBorder {padding: 10px 10px;
  2. border: 10px solid;}
  3. .FancyBorder-blue {border-color: blue;}
  4. .Dialog-title {margin: 0;
  5. font-family: sans-serif;}
  6. .Dialog-message {font-size: larger;}

组合对于定义为类的组件同样适用:

  1. function FancyBorder(props) {
  2. return (<div className={'FancyBorder FancyBorder-' + props.color}>
  3. {props.children}</div>);}
  4. function Dialog(props) {
  5. return (<FancyBorder color="blue"><h1 className="Dialog-title">{props.title}</h1>
  6. <p className="Dialog-message">{props.message}</p>{props.children}</FancyBorder>);}
  7. class SignUpDialog extends React.Component {
  8. constructor(props) {super(props);
  9. this.handleChange = this.handleChange.bind(this);
  10. this.handleSignUp = this.handleSignUp.bind(this);
  11. this.state = {login: ''}; }
  12. render() {
  13. return (<Dialog title="Mars Exploration Program" message="How should we refer to you?">
  14. <input value={this.state.login} onChange={this.handleChange} />
  15. <button onClick={this.handleSignUp}>Sign Me Up!</button></Dialog>);}
  16. handleChange(e) { this.setState({login: e.target.value});}
  17. handleSignUp() { alert(`Welcome aboard, ${this.state.login}!`);}}
  18. ReactDOM.render(<SignUpDialog />,document.getElementById('root'));
  1. .FancyBorder {padding: 10px 10px;
  2. border: 10px solid;}
  3. .FancyBorder-blue {border-color: blue;}
  4. .Dialog-title { margin: 0;
  5. font-family: sans-serif;}
  6. .Dialog-message {font-size: larger;}

那么继承呢?
在Facebook上,我们在数千个组件中使用react,并且我们还没有发现任何我们建议创建组件继承层次结构的用例。

props和composition为您提供了以明确和安全的方式定制组件外观和行为所需的所有灵活性。记住,组件可以接受任意的属性,包括原语值、反应元素或函数。

如果要在组件之间重用非UI功能,我们建议将其提取到单独的JavaScript模块中。组件可以导入它并使用该函数、对象或类,而不扩展它。

think of React React思维步骤

一、Start With A Mock 模拟:将UI分解为组件层次结构
第一件事是围绕模型中的每个组件(和子组件)绘制框,并为它们指定所有名称。如果和一个设计师合作,他们可能已经完成了!它们的photoshop层名称最终可能是您的反应组件的名称!
但是怎么知道什么应该是它自己的组件呢?只需使用相同的技术来决定是否应该创建一个新的函数或对象。其中一种技术是单一责任原则,也就是说,一个组件在理想情况下应该只做一件事。如果它最终长大了,就应该分解成更小的子成分。
由于经常向用户显示JSON数据模型,因此会发现,如果模型构建正确,那么您的UI(以及组件结构)将很好地映射。这是因为用户界面和数据模型倾向于遵循相同的信息体系结构,这意味着将用户界面分离为组件的工作通常是微不足道的。只需将它分解成只代表数据模型一部分的组件
1、在简单的应用程序。有五个组件我们斜体代表每个组件的数据。
   FilterableProductTable(橙色):包含完整的例子 ProductCategoryRow(青绿色):显示每个类别的标题  ProductTable(绿色):显示和过滤器根据用户输入的数据收集 SearchBar(蓝色):接收用户输入       ProductRow(红色):显示每个产品一行
既然已经确定了模拟组件,安排成一个层次结构。出现在另一个组件的组件模拟应该出现在层次结构:
FilterableProductTable可筛选产品表 SearchBar搜索栏
ProductTable产品表 ProductCategoryRow产品类别下 ProductRow产生器
二、在反应建立一个静态版本
2、拥有了组件层次结构,现在是实现应用程序的时候了。最简单的方法是构建一个采用数据模型并呈现UI但不具有交互性的版本。最好将这些过程分离开来,因为构建静态版本需要大量的输入,而不需要思考,并且添加交互性需要大量的思考,而不是大量的输入。原因:
要构建呈现数据模型的应用程序静态版本,需要构建重用其他组件并使用props传递数据的组件。props是一种将数据从父级传递到子级的方法。如果熟悉状态的概念,那么就不要使用状态来构建这个静态版本。状态仅保留用于交互,即随时间变化的数据。因为这是应用程序的静态版本,所以不需要它。
可以自上而下或自下而上构建。也就是说,可以从更高层次结构中的组件(即从filterableproducttable开始)或更低层次结构中的组件(productrow)开始。在更简单的例子中,自上而下通常更容易,而在更大的项目中,自下而上更容易在构建时编写测试。
在这个步骤的最后,将拥有一个可重用组件库,用于呈现数据模型。组件将只有render()方法,因为这是应用程序的静态版本。层次结构顶部的组件(filterableproductTable)将使用数据模型作为属性。如果更改了基础数据模型并再次调用reactdom.render(),则将更新UI。很容易看到您的UI是如何更新的,以及在哪里进行更改的,因为没有什么复杂的事情发生。React的单向数据流(也称为单向绑定)使所有内容保持模块化和快速。
3、简短的插曲:道具与状态
react中有两种类型的“模型”数据:props和state。理解两者之间的区别是很重要的;如果你不确定两者之间的区别,请浏览官方的反应文档

三、确定UI状态的最小(但完整)表示形式
要使UI交互,需要能够触发对基础数据模型的更改。反应使状态变得容易。
要正确构建应用程序,首先需要考虑应用程序所需的最小可变状态集。这里的钥匙是干的:不要重复你自己。计算出应用程序所需状态的绝对最小表示形式,并计算出所需的其他一切。例如,如果正在构建一个待办事项列表,只需在其周围保留一个待办事项数组;不要为计数保留单独的状态变量。相反,当想要呈现todo计数时,只需取todo items数组的长度。
想想示例应用程序中所有数据片段。我们有:产品原始清单、用户输入的搜索文本、复选框的值、筛选的产品列表。
让我们逐一检查一下,找出哪个是状态。只需对每个数据块提出三个问题:
它是通过props从父级传入的吗?如果是这样,它可能不是状态。
它是否会随着时间的推移而保持不变?如果是这样,它可能不是状态。
您能基于组件中的任何其他状态或属性来计算它吗?如果是这样,它就不是状态。
最初的产品列表作为道具传入,所以这不是状态。搜索文本和复选框似乎是状态,因为它们随时间变化,无法从任何内容计算出来。最后,过滤后的产品列表不是状态,因为它可以通过将原始产品列表与复选框的搜索文本和值相结合来计算。
最后,我们的状态是:用户输入的搜索文本、复选框的值

四、确定状态存在的区域
好的,已经确定了应用程序状态的最小集合是什么。接下来,需要确定哪个组件会变异,或者拥有这个状态。
记住:react是指沿着组件层次结构的单向数据流。可能无法立即确定哪个组件应拥有什么状态。这通常是新来者最难理解的部分,因此请遵循以下步骤来理解:
对于应用程序中的每个状态:
识别基于该状态呈现某些内容的每个组件。
找到一个公共所有者组件(在层次结构中需要状态的所有组件之上的单个组件)。
公共所有者或层次结构中更高级别的另一个组件应该拥有状态。
如果找不到拥有状态的组件,只需创建一个新组件来保持状态,并将其添加到公共所有者组件上方的层次结构中。
让我们为我们的应用程序运行此策略:
ProductTable需要根据状态筛选产品列表,SearchBar需要显示搜索文本和选中状态。
公共所有者组件是FilterableProductTable。
从概念上讲,筛选文本和选中值在filterableproductTable中存在是有意义的。
很酷,所以我们决定我们的状态是filterable productTable。首先,将实例属性this.state=filterText:“”,instockOnly:false添加到filterableproductTable的构造函数中,以反映应用程序的初始状态。然后,将filterText和instockOnly作为一个属性传递给productTable和searchbar。最后,使用这些属性过滤productTable中的行,并在搜索栏中设置表单字段的值。
可以开始了解应用程序的行为:将filterText设置为“ball”并刷新应用程序。您将看到数据表已正确更新

五、添加反向数据流
到目前为止,已经构建了一个应用程序,它可以作为属性和状态在层次结构中的函数正确呈现。现在是时候支持数据以另一种方式流动了:层次结构深处的表单组件需要更新filterableproductTable中的状态。
react使这个数据流显式化,以便于理解程序的工作方式,但它确实需要比传统的双向数据绑定更多的类型。
如果尝试在当前版本的示例中键入或选中该框,将看到该响应忽略了您的输入。这是有意的,因为已经将输入的值prop设置为始终等于从filterableproductTable传入的状态。
想想我们想要发生什么。希望确保每当用户更改表单时,都更新状态以反映用户输入。由于组件只应更新自己的状态,因此FilterableProductTable将向SearchBar传递回调,每当更新状态时,SearchBar都将触发回调。可以在输入上使用onchange事件来通知它。filterableProductTable传递的回调将调用setState(),并更新应用程序。
虽然这听起来很复杂,但实际上只是几行代码。而且你的数据在整个应用程序中是如何流动的