1、jsx = js+xml  是js的拓展语法

 从本质上讲,JSX 只是为 React.createElement(component, props, ...children) 函数提供的语法糖

 优点:1)执行的效率更快

    2)他是类型安全的,编译的过程中就行及时的发现错误

    3)在使用jsx的时候编写模板或更加简单和快速

   注意:jsx中HTML标签必须按照W3C的规范写  标签必须闭合

//不能使用一个普通的表达式作为 React 元素类型。如果你想使用普通表达式来表示元素类型,首先你需要将其赋值给大写的变量。这通常会出现在根据不同的 props 渲染不同的组件时:
//为了解决这个问题,首先需要将表达式赋值给一个以大写字母开头的变量。
import React from 'react';
import { PhotoStory, VideoStory } from './stories';

const components = {
  photo: PhotoStory,
  video: VideoStory
};

function Story(props) {
  // 错误!JSX 类型不能是表达式!!!!!
  return <components[props.storyType] story={props.story} />;
  // 正确!JSX 类型可以是一个以大写字母开头的变量.
  const SpecificStory = components[props.storyType];
  return <SpecificStory story={props.story} />;
//在 JavaScript 中,if 语句和 for 循环不是表达式,因此不能在 JSX 中直接使用。但你可以将他们放在附近的代码块中,例如:
function NumberDescriber(props) {
  let description;
  if (props.number % 2 == 0) {
    description = <strong>even</strong>;
  } else {
    description = <i>odd</i>;
  }
  return <div>{props.number} is an {description} number</div>;
}

  属性展开

//如果你已经有一个 object 类型的 props,并且希望在 JSX 中传入,你可以使用扩展操作符 ... 传入整个 props 对象。这两个组件是等效的:
function App1() {
  return <Greeting firstName="Ben" lastName="Hector" />;
}

function App2() {
  const props = {firstName: 'Ben', lastName: 'Hector'};
  return <Greeting {...props} />;
}
//你也可以挑选你的组件将使用的指定属性(props),同时使用展开运算符传递所有其他属性(props)。
const Button = props => {
  const { kind, ...other } = props;
  const className = kind === "primary" ? "PrimaryButton" : "SecondaryButton";
  return <button className={className} {...other} />;
};

const App = () => {
  return (
    <div>
      <Button kind="primary" onClick={() => console.log("clicked!")}>
        Hello World!
      </Button>
    </div>
  );
};
//在上面的例子中,kind 属性(props)被安全地使用,并且 不被 传递给 DOM 中的 <button> 元素。 所有其他属性(props)都通过 ... other 对象传递,使得这个组件非常灵活。 你可以看到它传递了onClick 和 children 属性(props)。
//展开(Spread) 属性可能很有用,但它们还可以轻松地将不必要的属性(props)传递给不关心它们的组件,或将无效的HTML属性传递给DOM。我们建议谨慎使用此语法!!!!!!!!

  React组件也可以返回一个元素数组

render() {
  // No need to wrap list items in an extra element!
  return [
    // Don't forget the keys :)
    <li key="A">First item</li>,
    <li key="B">Second item</li>,
    <li key="C">Third item</li>,
  ];
}

  Functions(函数) 作为 Children(子元素)

//通常情况下,嵌入到 JSX 中的 JavaScript 表达式会被认为是一个字符串、React元素 或者是这些内容的一个列表。然而, props.children 类似于其他的 props(属性) ,可以被传入任何数据,而不是仅仅只是 React 可以渲染的数据。例如,如果有自定义组件,其 props.children 的值可以是回调函数
function Repeat(props){
  let items = [];
  for(let i = 0;i<props.numTimes;i++){
    items.push(props.children(i))
  }  
  return <div>{items}</div>
}    
funxtion ListOfTenThings(){
  return (
    <Repeat numTimes={10}>
        {(index) => <div>this is item {index} in the list</div>}
    </Repeat>
  )
}        

  Booleans, Null, 和 Undefined 被忽略

//false,null,undefined,和 true 都是有效的的 children(子元素) 。但是并不会被渲染,下面的JSX表达式渲染效果是相同的
<div />
<div></div>
<div>{false}</div>
<div>{null}</div>
<div>{undefined}</div>
<div>{true}</div>

//在有条件性渲染 React 元素时非常有用。如果 showHeader 为 true 时,<Header />会被渲染:
<div>
  {showHeader && <Header />}
  <Content />
</div>

//需要注意的是“falsy”值,例如数值 0 ,仍然会被 React 渲染。例如,这段代码不会按照你预期的发生,因为当 props.messages 是一个空数组时 0 会被打印
<div>
  {props.messages.length &&
    <MessageList messages={props.messages} />
  }
</div>
//要修复这个问题,确保 && 之前的表达式总是布尔值:
<div>
  {props.messages.length > 0 &&
    <MessageList messages={props.messages} />
  }
</div>

//反过来,如果在输出中想要渲染 false ,true,null 或者 undefined ,你必须先将其转化为字符串:
<div>
  My JavaScript variable is {String(myVariable)}.
</div>

2、react 开发环境的搭建

  1)react.js  核心文件  (npm i react --save)

  2)react-dom.js 渲染页面中的DOM 当前文件依赖于react核心文件  (npm i react-dom --save)

  3)babel.js  ES6转换成ES5   jsx语法转换成js  现今浏览器进行代码的兼容   (npm i babel-standalone --save)

3、注释/多行标签需要有一个父元素包裹

  let MyDOm = (<div>
    {/*此处是注释*/} 
    <div>上面是注释</div>
  </div>)

  ReactDOM.render(MyDom,document.getElementId("root"))

4、hook

  export default () => {

  const [count,setCount] = useState(0)

  //const [count,setCount] = useState(() = > {return 0})    同上

  return (<div>

    {count}

    <button onClick={() = > setConut(x => x+1)}> +1 </button> 

  </div>)

  }

5、可以用 花括号 把任意的 JavaScript 表达式 嵌入到 JSX 中。例如,2 + 2

6、如果是空标签,您应该像 XML 一样,使用 />立即闭合它 :

const element = <img src={user.avatarUrl} />;

7、Babel 将JSX编译成 React.createElement() 调用

//下面的两个例子是是完全相同的:
const element = (
  <h1 className="greeting">
    Hello, world!
  </h1>
);

const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world!'
);

//React.createElement() 会执行一些检查来帮助你编写没有bug的代码,但基本上它会创建一个如下所示的对象:
// 注意: 这是简化的结构
const element = {
  type: 'h1',
  props: {
    className: 'greeting',
    children: 'Hello, world'
  }
};

8、函数式组件和累组件

//函数式组件
function Welcome (props){
  return <h1>Hello,{props.name}</h1>  
}
//类组件
class Welcome extends React.Component {
  render(){
    return <h1>Hello,{this.props.name}</h1>
  }  
}

9、纯函数和非纯函数 :React 组件都必须是纯函数,并禁止修改其自身 props 

//纯函数
function sum(a, b) {
  return a + b;
}
//非纯函数:它改变了自身的输入值
function withdraw(account, amount) {
  account.total -= amount;
}

10、state(状态)更新可能是异步的

  React 为了优化性能,有可能会将多个 setState() 调用合并为一次更新。

     因为 this.props 和 this.state 可能是异步更新的,你不能依赖他们的值计算下一个state(状态)。

//例如, 以下代码可能导致 counter(计数器)更新失败:
//错误
this.setState({
counter:this.state.counter + this.props.increment,
})
//要解决这个问题,应该使用第 2 种 setState() 的格式,它接收一个函数,而不是一个对象。该函数接收前一个状态值作为第 1 个参数, 并将更新后的值作为第 2 个参数:
//正确
this.setState((state,props)=>({
counter:state.counter + props.increment
}))

 11、定时器在componentMount中设置,需要在componentWillUNmount中销毁

 componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

12、正确使用state

  1)不要直接修改state

// 错误
this.state.comment = 'Hello';
// 正确
this.setState({comment: 'Hello'});

  2)state跟新可能是异步的

  React 为了优化性能,有可能会将多个 setState() 调用合并为一次更新。

  因为 this.props 和 this.state 可能是异步更新的,你不能依赖他们的值计算下一个state(状态)。

  例如, 以下代码可能导致 counter(计数器)更新失败:

// 错误
this.setState({
  counter: this.state.counter + this.props.increment,
});
//要解决这个问题,应该使用第 2 种 setState() 的格式,它接收一个函数,而不是一个对象。该函数接收前一个状态值作为第 1 个参数, 并将更新后的值作为第 2 个参数:
// 正确
this.setState((state, props) => ({
  counter: state.counter + props.increment
}));
//我们在上面使用了一个箭头函数,但是也可以使用一个常规的函数:
// 正确
this.setState(function(state, props) {
  return {
    counter: state.counter + props.increment
  };
});

  3)state跟新会被合并

  当你调用 setState(), React 将合并你提供的对象到当前的状态中。

  合并是浅合并,所以 this.setState({comments}) 不会改变 this.state.posts 的值,但会完全替换this.state.comments 的值。

13、处理事件

  1)react 事件使用驼峰命名,而不是全部小写。

  2)通过 JSX , 你传递一个函数作为事件处理程序,而不是一个字符串。

//例如,HTML:
<button onclick="activateLasers()">
  Activate Lasers
</button>
//在 React 中略有不同:
<button onClick={activateLasers}>
  Activate Lasers
</butto//实事件处理:react绑定事件用小驼峰,在绑定函数的时候不能加(),不然会立即执行
//1、修改this的指向
//2、bind方法原地绑定
//3、函数通过箭头函数进行创建
//4、constructor中提前绑定
//5、把事件调用写成箭头函数的形式

class Com extends React.COmponent{
    constructor(props){
         super(props)
         this.funcC = this.funcC.bind(this)
    }

    funcA(){
        console.log(this)           
    }
    funcB = ()=>{
        console.log(this)           
    }
    funcC(){
        console.log(this)           
    }
    funcD(){
        console.log(this)           
    }
    funcE = (this,i) =>{
        console.log(i)  
        console.log(this)           
    }
    funcF = (i,e) =>{
        console.log(i)  
        console.log(e)           
    }

     render(){
         return(
         <div>
          //我是组件
           <button onClick = {this.funcA.bind(this)}>bind方式绑定</button>
           <button onClick = {this.funcB}>bind方式绑定</button>
           <button onClick = {this.funcC}>bind方式绑定</button>
           <button onClick = {() => {this.funcD()}}>bind方式绑定</button>
           <button onClick = {this.funcE.bind(this,'我是参数1','我是参数2')}>参数1</button>
           <button onClick = {this.funcF('我是参数1','我是参数2',e)}>参数2</button>
         </div>
         )
    }
}

  3)另一个区别是,在 React 中你不能通过返回 false(愚人码头注:即 return false; 语句) 来阻止默认行为。必须明确调用 preventDefault 。

//例如,对于纯 HTML ,要阻止链接打开一个新页面的默认行为,可以这样写:
<a href="#" onclick="console.log('The link was clicked.'); return false">
  Click me
</a>
//在 React 中, 应该这么写:
function ActionLink() {
  function handleClick(e) { //这里, e 是一个合成的事件。
    e.preventDefault();
    console.log('The link was clicked.');
  }

  return (
    <a href="#" onClick={handleClick}>
      Click me
    </a>
  );
}

  4)this指向问题

//在JSX回调中你必须注意 this 的指向。 在 JavaScript 中,类方法默认没有 绑定 的。如果你忘记绑定 this.handleClick 并将其传递给onClick,那么在直接调用该函数时,this 会是 undefined 。
//这不是 React 特有的行为;这是 JavaScript 中的函数如何工作的一部分。 一般情况下,如果你引用一个后面没跟 () 的方法,例如 onClick={this.handleClick} ,那你就应该 绑定(bind) 该方法。

//方法1:bind方法
class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // 这个绑定是必要的,使`this`在回调中起作用
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(state => ({
      isToggleOn: !state.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

//方法2:实验性的 属性初始化语法 ,使用属性初始值设置来正确地 绑定(bind) 回调:
class LoggingButton extends React.Component {
  // 这个语法确保 `this` 绑定在 handleClick 中。
  // 警告:这是 *实验性的* 语法。
  handleClick = () => {
    console.log('this is:', this);
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>
    );
  }
}

//方法3:回调中使用一个 箭头函数:
//这个语法的问题是,每次 LoggingButton 渲染时都创建一个不同的回调。在多数情况下,没什么问题。然而,如果这个回调被作为 prop(属性) 传递给下级组件,这些组件可能需要额外的重复渲染。我们通常建议在构造函数中进行绑定,以避免这类性能问题。
class LoggingButton extends React.Component {
  handleClick() {
    console.log('this is:', this);
  }

  render() {
    // This syntax ensures `this` is bound within handleClick
    return (
      <button onClick={(e) => this.handleClick(e)}>
        Click me
      </button>
    );
  }
}

  4)将参数传递给事件处理程序

//在循环内部,通常需要将一个额外的参数传递给事件处理程序。 例如,如果 id 是一个内联 ID,则以下任一方式都可以正常工作:
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

//上面两个例子中,参数 e 作为 React 事件对象将会被作为第二个参数进行传递。通过箭头函数的方式,事件对象必须显式的进行传递,但是通过 bind 的方式,事件对象以及更多的参数将会被隐式的进行传递。

14、条件渲染

  1)声明一个变量并使用一个 if 语句是一个有条件地渲染组件

render() {
    const isLoggedIn = this.state.isLoggedIn;
    let button;

    if (isLoggedIn) {   //if语法
      button = <LogoutButton onClick={this.handleLogoutClick} />;
    } else {
      button = <LoginButton onClick={this.handleLoginClick} />;
    }

    return (
      <div>
        <Greeting isLoggedIn={isLoggedIn} />
        {button}
      </div>
    );
  }

  2)使用逻辑&&操作符的内联if用法

//您可以 在JSX中嵌入任何表达式 ,方法是将其包裹在花括号中。这也包括 JavaScript 逻辑 && 运算符。 它有助于有条件地包含一个元素:
function Mailbox(props) {
  const unreadMessages = props.unreadMessages;
  return (
    <div>
      <h1>Hello!</h1>
      {unreadMessages.length > 0 &&
        <h2>
          You have {unreadMessages.length} unread messages.
        </h2>
      }
    </div>
  );
}

const messages = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(
  <Mailbox unreadMessages={messages} />,
  document.getElementById('root')
);

  3)另一个用于条件渲染元素的内联方法是使用 JavaScript 的条件操作符 condition ? true : false 。

  4)防止组件渲染

  在极少数情况下,您可能希望组件隐藏自身,即使它是由另一个组件渲染的。为此,返回 null 而不是其渲染输出。

//从组件的 render 方法返回 null 不会影响组件生命周期方法的触发。 例如, componentWillUpdate 和 componentDidUpdate 仍将被调用。
function WarningBanner(props) {
  if (!props.warn) {
    return null;
  }

  return (
    <div className="warning">
      Warning!
    </div>
  );
}

 15、列表(list)和键(keys)

//在数组中使用的 keys 必须在它们的同辈之间唯一。然而它们并不需要全局唯一。我们可以在操作两个不同数组的时候使用相同的 keys 
//键是React的一个内部映射,但其不会传递给组件的内部。如果你需要在组件中使用相同的值,可以明确使用一个不同名字的 prop 传入。
//Post 组件可以读取 props.id,但是不能读取 props.key 。
function Post(props) {
  const posts = props.posts;
  return (
    <ul>
      {numbers.map((post) =>
        <ListItem key={post.id}
                  id={post.id}  
                  title={post.title} />
      )}
    </ul>
  );
}

16、表单

  1)受控组件

  概念:在 HTML 中,表单元素如 <input><textarea> 和 <select> 表单元素通常保持自己的状态,并根据用户输入进行更新。而在 React 中,可变状态一般保存在组件的 state(状态) 属性中,并且只能通过 setState() 更新。

        我们可以通过使 React 的 state 成为 “单一数据源原则” 来结合这两个形式。然后渲染表单的 React 组件也可以控制在用户输入之后的行为。这种形式,其值由 React 控制的输入表单元素称为“受控组件”。

  受控组件的替代方案:

  有时使用受控组件有些乏味,因为你需要为每一个可更改的数据提供事件处理器,并通过 React 组件管理所有输入状态。当你将已经存在的代码转换为 React 时,或将 React 应用程序与非 React 库集成时,这可能变得特别烦人。在这些情况下,您可能需要使用不受控的组件,用于实现输入表单的替代技术

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});  //this.setState({value: event.target.value.toUpperCase()});
  }

  handleSubmit(event) {
    alert('A name was submitted: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

  2)textarea

  <textarea> 的赋值使用 value 属性替代。这样一来,表单中 <textarea> 的书写方式接近于单行文本输入框 

  3)select标签

//React 中,并不使用这个 selected 属性,而是在根 select 标签中使用了一个 value 属性。这使得受控组件使用更方便,因为你只需要更新一处即可。例如:
class FlavorForm extends React.Component {
  constructor(props) {
    super(props)
    this.state = { value: 'coconut' }

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }
  handleChange(event) {
    this.setState({ value: event.target.value })
  }
  handleSubmit(event) {
    alert('Your favorite flavor is :' + this.state.value)
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Pick your favorite flavor:
      <select value={this.state.value} onChange={this.handleChange}>   //<select multiple={true} value={['B', 'C']}>   您可以将一个数组传递给 value 属性,允许你在 select 标签中选择多个选项
            <option value="grapefruit">Grapefruit</option>
            <option value="lime">lime</option>
            <option value="coconut">coconut</option>
       <option value="mango">mango</option>
          </select>
        </label>
        <input type="submit" value="Submit" />
      </form>
    )
  }
}

  4)file input 标签 (不受控 组件)

  在HTML中, <input type="file"> 可以让用户从设备存储器中选择一个或多个文件上传到服务器,或者通过 JavaScript 使用 File API 操作。

<input type="file" />

  5)处理多个输入元素

  当您需要处理多个受控的 input 元素时,您可以为每个元素添加一个 name 属性,并且让处理函数根据 event.target.name 的值来选择要做什么。

class Reservation extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isGoing: true,
      numberOfGuests: 2
    };

    this.handleInputChange = this.handleInputChange.bind(this);
  }

  handleInputChange(event) {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked : target.value;
    const name = target.name;
    //ES6 计算的属性名称语法来更新与给定输入名称相对应的 state(状态) 键:
    //下面这段代码等价于ES5代码:
    //var partialState = {};
    //partialState[name] = value;
    // this.setState(partialState);
    this.setState({ 
      [name]: value
    });
  }

  render() {
    return (
      <form>
        <label>
          Is going:
          <input
            name="isGoing"
            type="checkbox"
            checked={this.state.isGoing}
            onChange={this.handleInputChange} />
        </label>
        <br />
        <label>
          Number of guests:
          <input
            name="numberOfGuests"
            type="number"
            value={this.state.numberOfGuests}
            onChange={this.handleInputChange} />
        </label>
      </form>
    );
  }
}

  6)如果您正在寻找一个完整的解决方案,包括验证、跟踪访问的字段以及处理表单提交,那么 Formik 是最受欢迎的选择之一。但是,它建立在受控组件和管理状态的相同原则之上 - 所以不要忽视学习它们。

17、状态提升

  在一个 React 应用中,对于任何可变的数据都应该循序“单一数据源”原则。通常情况下,state 首先被添加到需要它进行渲染的组件。然后,如果其它的组件也需要它,你可以提升状态到它们最近的祖先组件。你应该依赖 从上到下的数据流向 ,而不是试图在不同的组件中同步状态。(官方demo: http://react.html.cn/docs/lifting-state-up.html)

18、组合(Composition)VS 继承(Inheritance)

  在 Facebook ,我们在千万的组件中使用 React,我们还没有发现任何用例,值得我们建议你用继承层次结构来创建组件。

  使用 props(属性) 和 组合已经足够灵活来明确、安全的定制一个组件的外观和行为。切记,组件可以接受任意的 props(属性) ,包括原始值、React 元素,或者函数。

//使用特别的 children prop 来直接传递 子元素,props.children自带props,代表子组件的内容
function Dialog(props) {
  return (
    <FancyBorder color="blue">
      <h1 className="Dialog-title">
        {props.title}
      </h1>
      <p className="Dialog-message">
        {props.message}
      </p>
      {props.children}
    </FancyBorder>
  );
}

class SignUpDialog extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.handleSignUp = this.handleSignUp.bind(this);
    this.state = {login: ''};
  }

  render() {
    return (
      <Dialog title="Mars Exploration Program"
              message="How should we refer to you?">
        <input value={this.state.login}
               onChange={this.handleChange} />

        <button onClick={this.handleSignUp}>
          Sign Me Up!
        </button>
      </Dialog>
    );
  }

  handleChange(e) {
    this.setState({login: e.target.value});
  }

  handleSignUp() {
    alert(`Welcome aboard, ${this.state.login}!`);
  }
}

19、优化性能

  1)ES6 对于数组支持展开语法 ,使得解决上述问题更加简单。如果你使用的是Create React App,默认支持该语法

handleClick() {
  this.setState(state => ({
    words: [...state.words, 'marklar'],
  }));
};
//可以以一种简单的方式重写上述代码,使得改变对象的同时不会突变对象,例如,如果有一个 colormap 的对象并且编写一个函数将 colormap.right 的值改为 'blue'
function updateColorMap(colormap) {
  colormap.right = 'blue';
}
//在不突变原来的对象的条件下实现上面的要求,我们可以使用Object.assign 方法:
function updateColorMap(colormap) {
  return Object.assign({}, colormap, {right: 'blue'});
}
//updateColorMap 现在返回一个新的对象,而不是修改原来的对象。Object.assign 属于ES6语法,需要 polyfill。
//JavaScript提案添加了对象展开符 ,能够更简单地更新对象而不突变对象。
function updateColorMap(colormap) {
  return {...colormap, right: 'blue'};
}

  2)使用 Immutable 数据结构  https://github.com/immutable-js/immutable-js

//使用 Immutable 数据结构
const SomeRecord = Immutable.Record({ foo: null });
const x = new SomeRecord({ foo: 'bar' });
const y = x.set('foo', 'baz');
const z = x.set('foo', 'bar');
x === y; // false
x === z; // true

 

posted on 2020-05-08 18:51  zhengyulu  阅读(128)  评论(0编辑  收藏  举报