JSX 进阶 列表渲染所谓列表渲染,是指当写入 jsx 的数据是一个React DOM数组时,最终会生成一个列表。
这个例子生成一个简单的列表:
1 2 3 4 5 6 const arr = [1 ,2 ,3 ,4 ,5 ];const lists = arr.map (x => <li > {x}</li > );const el = (<ul > {lists}</ul > );
但我们很快收到了一个警告:
Each child in a list should have a unique “key” prop.
意思是当你创建一个元素时,必须包括一个特殊的 key 属性。
我们可以指定 key 来解决问题:
1 2 3 4 5 const arr = [1 ,2 ,3 ,4 ,5 ];const lists = arr.map (x => <li key ={x} > {x}</li > );const el = (<ul > {lists}</ul > );
问题是解决了,那为什么加一个 key 呢?
因为,key 帮助 React 识别哪些元素改变了,比如被添加或删除。
对于同一个数组,每个React DOM 应该具有不同的 key。
条件渲染 使用 if 语句使用 if 语句就可以快速实现条件渲染:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 let state = true ;function render ( ) { let el = null ; if (state) { el = <p > state is true.</p > ; } else { el = <p > state is false.</p > ; } let editState = ( ) => (state = !state); ReactDOM .render ( <div > {el} <button onClick ={editState} > 更改状态</button > {" "} </div > , document .querySelector ("#container" ) ); } render ();
使用运算符除了使用 if 语句,还可以使用运算符来实现条件渲染:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 let state = true ;function render ( ) { let editState = ( ) => (state = !state); ReactDOM .render ( <div > {state ? <p > state is true.</p > : <p > state is false.</p > } <button onClick ={editState} > 更改状态</button > {" "} </div > , document .querySelector ("#container" ) ); } render ();
元素的不可变性在上面的两个例子,你应该发现了这样一个问题:元素下面的按钮无论怎么点,变量是改变了,但视图却没有刷新。
需要注意一点,React 元素是不可变的。一旦被创建,你就无法更改它的子元素或者属性。唯一修改的方式便是创建一个全新的元素,并将其传入 ReactDOM.render() 。但是,React DOM 会将元素和它的子元素与它们之前的状态进行比较,并只会进行必要的更新来使 DOM 达到预期的状态。
所以要想让界面发生变化,我们需要修改一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 let state = true ;function render ( ) { let editState = ( ) => { state = !state; render (); }; ReactDOM .render ( <div > {state ? <p > state is true.</p > : <p > state is false.</p > } <button onClick ={editState} > 更改状态</button > </div > , document .querySelector ("#container" ) ); } render ();
这样,再点击按钮,视图就发生变化了。
但这看起来有些麻烦,使用过 Vue 的同学应该希望当变量发生改变时,UI也会自动更新吧。
后文会提到组件的状态,它就可以自动调用 render,来实现UI的自动更新。
表单输入绑定 受控组件在HTML中,像 input, textarea 和 select 等表单元素会维持自身状态,并会根据用户输入进行更新。
我们可以将表单元素的值绑定在变量中,并且监听表单事件的更新,就可以将表单输入和表单对象进行同步。
在这种操作下,input、textarea、select 像是被父组件控制了一样,称为 受控组件 。
处理表单输入处理 input type=text:
1 2 3 4 5 6 7 8 9 10 11 12 let handleInputChange = (name ) => (e ) => { form[name] = e.target .value ; render (); }; let textInput = ( <input type ="text" value ={form[ "text "]} onChange ={handleInputChange( "text ")} /> );
处理 textarea:
这里和原始的 html 有点不一样,它使用 value 进行传值,使用 onChange 进行更新
1 2 3 4 5 6 7 8 let handleInputChange = (name ) => (e ) => { form[name] = e.target .value ; render (); }; let textAreaInput = ( <textarea value ={form[ "textarea "]} onChange ={handleInputChange( "textarea ")} /> );
处理 radio:
1 2 3 4 5 6 7 8 9 10 11 12 let handleCheckState = (name ) => (e ) => { form[name] = e.target .checked ; render (); }; let radioInput = ( <radio name ="radio" checked ={form[ "radio "]} onChange ={handleCheckState( "textarea ")} /> );
处理 input type=checkbox:
1 2 3 4 5 6 7 8 9 10 11 12 13 let handleCheckState = (name ) => (e ) => { form[name] = e.target .checked ; render (); }; let checkboxInput = ( <input name ="check" type ="checkbox" checked ={form[ "checkbox "]} onChange ={handleCheckState( "checkbox ")} /> );
处理 select:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 let handleInputChange = (name ) => (e ) => { form[name] = e.target .value ; render (); }; let selectInput = ( <select value ={form[ "select "]} onChange ={handleInputChange( "select ")}> <option > 星期一</option > <option > 星期二</option > <option > 星期三</option > <option > 星期四</option > <option > 星期五</option > <option > 星期六</option > <option > 星期日</option > </select > );
不可修改的输入如果只绑定了 value,而没有绑定更新的事件,那么这个表单将无法接受用户的输入:
1 let readOnlyInput = <input value ={form[ "readonly "]} /> ;
但是,如果表单输入属性的 value 被指定为 undefined 或 null,那么表单便可以修改了:
1 let writableInput = <input value ={form[ "writable "]} /> ;
完整的示例1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 let form = { readonly : "123" , }; let handleInputChange = (name ) => (e ) => { form[name] = e.target .value ; render (); }; let handleCheckState = (name ) => (e ) => { form[name] = e.target .checked ; render (); }; let textInput = ( <input type ="text" value ={form[ "text "]} onChange ={handleInputChange( "text ")} /> ); let textAreaInput = ( <textarea value ={form[ "textarea "]} onChange ={handleInputChange( "textarea ")} /> ); let radioInput = ( <input name ="radio" type ="radio" checked ={form[ "radio "]} onChange ={handleCheckState( "radio ")} /> ); let checkboxInput = ( <input name ="check" type ="checkbox" checked ={form[ "checkbox "]} onChange ={handleCheckState( "checkbox ")} /> ); let selectInput = ( <select value ={form[ "select "]} onChange ={handleInputChange( "select ")}> <option > 星期一</option > <option > 星期二</option > <option > 星期三</option > <option > 星期四</option > <option > 星期五</option > <option > 星期六</option > <option > 星期日</option > </select > ); let readOnlyInput = <input value ={form[ "readonly "]} /> ;let writableInput = <input value ={form[ "writable "]} /> ;function render ( ) { ReactDOM .render ( <div > {textInput} {textAreaInput} {radioInput} {checkboxInput} {selectInput} {readOnlyInput} {writableInput} </div > , document .querySelector ("#container" ) ); } render ();
小结表单元素 绑定属性 绑定方法 事件回调新值 input[type=text]valueonChangeevent.target.valueinput[type=checkbox]checkedonChangeevent.target.checkedinput[type=radio]checkedonChangeevent.target.checkedtextareavalueonChangeevent.target.valueselectvalueonChangeevent.target.value
片段有些时候,我们希望将一些 React DOM 组合起来,像这样:
但默认情况下,直接把它们写在一起是会报错的:
1 const el = (<a > 1</a > <a > 2</a > );
因为 React 规定,一个 jsx 必须有一个根节点。
然后条件反射,就直接加上了一个根 div节点:
1 const el = (<div > <a > 1</a > <a > 2</a > </div > );
但有时候并不是开发者想要的。
比如,开发者想把不同的 a 链接写到一块,这时候有另外一些代码:
1 2 const el2 = (<div > <a > 3</a > <a > 4</a > </div > );const el3 = (<div > {el}{el2}</div > );
这样一写,这些链接便被分成了两行了。
这种情况下,我们就可以使用 片段 (Fragments ) 来解决这个问题。
片段是一个 React 的组件,它可以当成一个节点使用,但不会渲染出任何 HTML DOM。
上面的例子就可以这样写:
1 2 3 const el = (<React.Fragment > <a > 1</a > <a > 2</a > </React.Fragment > );const el2 = (<React.Fragment > <a > 3</a > <a > 4</a > </React.Fragment > );const el3 = (<div > {el}{el2}</div > );
用过 Vue 的朋友应该很容易发现,这类似于 Vue 的 template 组件。