了解状态 在React中,State
是一种用来存储组件内部状态的变量。
与上一章的Props
在组件间传递状态相对的,状态这一变量代表了组件内部的情况,可以被看做组件的“内存”。
组件内部的状态 作为组件内部情况的代表,每当状态的量发生改变时,React会重新渲染整个组件。
组件与状态更新 综上,状态作为一种持久化的局部变量,允许我们在更改组件内容的同时重新渲染整个组件。
React状态 使用状态,就意味着我们用声明式的代码构造了用户界面,并让其随着时间推移发生变化。
状态与声明式代码 状态工作流程 我们定义组件的状态变量,并在处理用户交互逻辑的handler
函数中更新状态。
定义状态变量以及handler 当用户与界面交互时,触发handler
并更新状态。React检测到状态更新,于是重新渲染组件。
React重新渲染 状态与道具
状态与道具 使用状态 何时使用状态
使用状态指导方针 状态基本语法 1
2
3
4
const [ value , SetValue ] = useState ( "init value" );
console . log ( value ); // init value
SetValue ( "next value" );
console . log ( value ) // next value
Copy 我们使用React的useState()
函数生成一个状态的数组。数组第一位是状态变量本身,可以使用useState()
函数的传入值来指定其初始值;数组第二位是更新状态变量的函数。实际应用中,要想重新渲染组件 ,就要使用数组中的更新函数 来对状态进行更新。将状态定义成let
或var
都是无效的操作。
我们知道状态代表了组件内部的状态,因此在React中,状态只能在组件内开头定义 。
实际使用中,我们还会使用更安全的方法来更新状态变量。
1
SetValue (( value )=> value + 1 );
Copy 派生状态 实际应用过程中,有些变量可以由状态计算得到 ,并在重新渲染的过程中得到更新 。我们可以创建这种类型的派生状态
。
1
2
3
4
5
6
7
8
9
const [ date , SetDate ] = useState ( new Date ());
const month = date . getMonth ()
const exdate = date . getDate ()
const day = date . getDay ()
return (
< p > Today is { day } of { month } { exdate }.</ p >
)
Copy 实际编写过程中,为了减少状态管理的难度 ,我们应该尽量创建派生状态。
控制元素 我们可以使用状态量和设置状态函数来控制一些HTML元素的value
和handler
。
1
2
3
4
5
< input
type = { "text" }
value = { count }
onChange = {( e ) => handleInputChange ( e )}
/>
Copy 在这个HTML的input
文本框元素中,我们将其文本值设置为count
状态,并在其值改变时调用handleInputChange()
函数,并在其中更新count
状态。
1
2
3
4
function handleInputChange ( e ) {
const t = e . target . value ;
setCount ( t );
}
Copy 状态提升 实际开发过程中,我们会在不同组件中创建大量 的状态变量。然而由于React的单向通信 原则,我们无法在各个组件之间 使用道具来传递状态变量。因此,要让各个组件之间的状态互相同步 ,我们必须要将某个组件的状态提升到父节点上。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Count ({ count , setCount }) {
function handleInputChange ( e ) {
const t = e . target . value ;
setCount ( t );
}
return (
< div style = {{ textAlign : "center" }}>
< button onClick = {() => setCount (( c ) => Number ( c ) - 1 )}> - </ button >
< input
type = { "text" }
value = { count }
onChange = {( e ) => handleInputChange ( e )}
/>
< button onClick = {() => setCount (( c ) => Number ( c ) + 1 )}> + </ button >
</ div >
);
}
Copy 在这个Count
组件中,我们使用到了count
状态,但是却没有在Count
组件内定义它,而是在它的父组件App
内定义它。
1
2
3
4
5
6
7
8
9
10
11
12
function App () {
const [ step , setStep ] = useState ( 0 );
const [ count , setCount ] = useState ( 0 );
return (
<>
< Step step = { step } setStep = { setStep } />
< Count count = { count } setCount = { setCount } />
< DateP step = { step } count = { count } />
< Reset setStep = { setStep } setCount = { setCount } step = { step } count = { count } />
</>
);
}
Copy 这样,所有App
组件的子组件都可以使用count
状态。当Count
组件更新count
状态时,所有App
组件中的子组件也将正常重新渲染。
状态实战 我们制作一个日历程序,可以通过多种交互方式改变日期的描述。
首先分析程序中的状态。可以发现:
step
和count
会根据交互而改变,并在不同的组件中被更新;真实日期可以根据step
和count
计算得出。 因此,我们在父组件App
中定义step
和count
状态,并将其作为道具传递给子组件。
1
2
3
4
5
6
7
8
9
10
11
12
function App () {
const [ step , setStep ] = useState ( 0 );
const [ count , setCount ] = useState ( 0 );
return (
<>
< Step step = { step } setStep = { setStep } />
< Count count = { count } setCount = { setCount } />
< DateP step = { step } count = { count } />
< Reset setStep = { setStep } setCount = { setCount } step = { step } count = { count } />
</>
);
}
Copy 在DataP
组件中,我们用step
和count
计算日期; 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function DateP ({ step , count }) {
let input = step * count ;
let date = new Date ();
let temp = date . getDate () + input ;
date . setDate ( temp );
date = date . toDateString ();
return (
< p style = {{ margin : 10 , textAlign : "center" }}>
{ input !== 0 ? Math . abs ( input ) + " " : "" }
{ input !== 0 ? ( input > 0 ? "Days from today " : "Days ago " ) : "Today " }
{ input >= 0 ? "is" : "was" } { date }
</ p >
);
}
Copy 在Step
和Count
组件中,我们控制HTML元素,将状态和交互绑定在一起。
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
function Step ({ step , setStep }) {
function handleInputChange ( e ) {
const n = e . target . value ;
setStep ( n );
}
return (
< div style = {{ textAlign : "center" }}>
< input
type = { "range" }
min = { "1" }
max = { "10" }
value = { step }
onChange = {( e ) => handleInputChange ( e )}
/>
< span style = {{ margin : 10 }}> Step : { step }</ span >
</ div >
);
}
function Count ({ count , setCount }) {
function handleInputChange ( e ) {
const t = e . target . value ;
setCount ( t );
}
return (
< div style = {{ textAlign : "center" }}>
< button onClick = {() => setCount (( c ) => Number ( c ) - 1 )}> - </ button >
< input
type = { "text" }
value = { count }
onChange = {( e ) => handleInputChange ( e )}
/>
< button onClick = {() => setCount (( c ) => Number ( c ) + 1 )}> + </ button >
</ div >
);
}
Copy 最后,我们添加一个Reset
按钮组件,按下时就清空count
和step
状态。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Reset ({ setStep , setCount , step , count }) {
function handleReset () {
setStep ( 0 );
setCount ( 0 );
}
return (
( step !== 0 || count !== 0 ) && (
< div style = {{ display : "flex" , justifyContent : "center" }}>
< button onClick = { handleReset }> Reset </ button >
</ div >
)
);
}
Copy