vue和react中props變化后如何修改state
vue和react中props變化后修改state
如果只想在 state 更改時重新計算某些數(shù)據,比如搜索框案例。

vue
<template>
<div>
<input type="text" v-model="filterText">
<ul>
<li v-for="item in filteredList" :key="item.id">
{{ item.text }}
</li>
</ul>
</div>
</template>
<script>
export default {
props: {
list: {
type: Array,
default: () => ([])
}
},
data () {
return {
filterText: ''
}
},
computed: {
filteredList () {
return this.list.filter(item => item.text.includes(this.filterText))
}
}
}
</script>
react
import React, { PureComponent } from 'react';
class Example extends PureComponent {
state = {
filterText: ''
};
handleChange = event => {
this.setState({
filterText: event.target.value
})
}
render() {
const filteredList = this.filter(this.props.list, this.state.filterText)
return (
<>
<input
type="text"
onChange={this.handleChange}
value={this.state.filterText} />
<ul>
{
filteredList.map(
item => <li key={item.id}>{item.text}</li>
)
}
</ul>
</>
);
}
}
如果你想在 prop 更改時“重置”某些 state,比如隨機默認值案例

vue
Vue提供了一種更通用的方式來觀察和響應Vue實例上的數(shù)據變動:偵聽屬性 watch。
<template>
<div>
<input type="text" v-model="text">
</div>
</template>
<script>
export default {
props: {
email: {
type: String,
default: ''
}
},
data () {
return {
text: ''
}
},
watch: {
email: {
immediate: true,
handler (value) {
this.text = value
}
}
}
}
</script>react
React生命周期 getDerivedStateFromProps 會在調用 render 方法之前調用,并且在初始掛載及后續(xù)更新時都會被調用。它應返回一個對象來更新 state,如果返回 null 則不更新任何內容。
父組件重新渲染時觸發(fā),請注意,不管原因是什么,都會在每次渲染前觸發(fā)此方法。
class Example extends Component {
state = {
text: ''
};
handleChange = (event) => {
this.setState({
text: event.target.value
})
}
static getDerivedStateFromProps(nextProps, prevState) {
if (prevState.email !== nextProps.email) {
return {
text: nextProps.email,
email: nextProps.email
}
}
return {text: prevState.text}
}
render() {
return (
<>
<input
type="text"
onChange={this.handleChange}
value={this.state.text} />
</>
);
}
}
改進
直接復制 prop 到 state 是一個非常糟糕的想法。這兩者的關鍵在于,任何數(shù)據,都要保證只有一個數(shù)據來源,而且避免直接復制它。
vue
<template>
<div>
<input type="text" :value="value" @input="handleInput">
</div>
</template>
<script>
export default {
props: {
value: {
type: String,
default: ''
}
},
methods: {
handleInput (e) {
this.$emit('input', e.target.value)
}
}
}
</script>
<template>
<div id="app">
<Example v-model="email"/>
<button @click="handleClick">默認值</button>
</div>
</template>
<script>
import Example from './components/Example.vue'
export default {
components: {
Example
},
data () {
return {
email: ''
}
},
methods: {
handleClick () {
this.email = String(Math.random())
}
}
}
</script>react
function Example (props) {
return <input onChange={props.onChange} value={props.email} />;
}
class App extends React.Component {
state = {
email: ''
}
handleClick = () => {
this.setState({
email: String(Math.random())
})
}
handleChange = (event) => {
this.setState({
email: event.target.value
})
}
render() {
return (
<>
<Example email={this.state.email} onChange={this.handleChange} />
<div>
<button onClick={this.handleClick}>默認值</button>
</div>
</>
);
}
}
react改變state必須知道的知識點
react可以通過this.state.xx的方式直接獲取state,但是當我們修改state的時候,往往有許多的坑。
1.不能直接修改state
組件修改state,并不會重新觸發(fā)render。例如:
?//錯誤
this.state.title='attend';
//正確
this.setState({title:'attend'});2.state的更新是異步的
調用setState時,組件state并不會立即改變,只是把要修改的狀態(tài)放入事件隊列當中,為了彌補這個問題,使用另一種 setState() 的形式,接受一個函數(shù)。
這個函數(shù)將接收前一個狀態(tài)作為第一個參數(shù),應用更新時的 props 作為第二個參數(shù),代碼如下:
//正確
this.setState((prevState, props)=>({
?? ?counter: prevState.counter + 1
}))3.state的更新是一個合并的過程
當調用setState()修改組件的狀態(tài)時,只需要傳入發(fā)生改變的state,而不是完整的state,因為組件state的更新是一個合并的過程:
this.state = {
?? ?title: 'React',
?? ?content: 'React is an wondeful JS library'
}當只需要修改title時,只需要將修改的title傳給setState即可:
this.setState({title:'ReactJs'});react會合并最新的title到原來的狀態(tài),同時保留原來狀態(tài)的content,最終合并state為:
this.state = {
?? ?title: 'ReactJs',
?? ?content: 'React is an wondeful Js library'
}state與不可變對象
react官方把state當成不可變對象,一方面直接修改this.state,組件并不會重新render;另一方面,state中包含的所有狀態(tài)都應該是不可變的對象,state當中的某一個狀態(tài)發(fā)生變化時,應該重新創(chuàng)建這個狀態(tài)對象,而不是直接修改原來的state狀態(tài),那么當狀態(tài)發(fā)生變化時,如何去創(chuàng)建新的狀態(tài)呢,我們根據狀態(tài)類型可以分為下面三種情況:
狀態(tài)類型為不可變類型
number、string、boolean、null、undefined
這種情況最簡單,因為狀態(tài)是不可變類型,所以直接給要修改的狀態(tài)賦一個新值即可,例如我們要修改的count為number型,title(string),success(boolean)三個狀態(tài):
this.setState({
?? ?count:1,
?? ?title:'React',
?? ?success:true
})狀態(tài)類型為數(shù)組
假如有一個數(shù)組類型的狀態(tài)books,當向books中增加一本書時。
//方法一:使用preState,concat創(chuàng)建新數(shù)組
?this.setState((prevState)=>({
?? ?books: prevState.books.concat(['React Guide'])
}))
//方法二:ES6 spread syntax
this.setState(prevState=>({
?? ?books:[...prevState,'React Guide']
}))當我們從books中截取部分元素作為新狀態(tài)時,可以用數(shù)組的slice方法:
this.setState(prevState=>({
?? ?books: prevState.books.slice(1,3);
}))當從books中過濾部分元素后,作為新狀態(tài)時,可以使用filter方法:
this.setState(prevState=>({
?? ?books: prevState.books.filter(item=>{
?? ? ?return item!='React';
?? ?})
}))【注意】不要使用push,pop,shift,unshift,splice等方法修改數(shù)組類型的狀態(tài),因為這些方法都是在原數(shù)組的基礎上修改的,而concat,slice,filter會返回一個新的數(shù)組。
狀態(tài)的類型是普通對象
(1) 使用es6的Object.assgin()方法
this.setState({
?? ?onwer: Object.assgin({},preState.onwer,{name:'Jason'});
})(2) 使用對象擴展語法(Object spread properties):
this.setState(preState=>{
?? ?owner: {...preState.owner, name:'Jason'}
})總結
創(chuàng)建新的狀態(tài)的關鍵是避免使用直接修改原對象的方法,這種方法在vue中稱為變異方法,而是使用可以返回一個新對象的方法,當然可以使用Immutable的JS庫(Immutable.js)實現(xiàn)類似的效果。
思考
為什么React推薦組件狀態(tài)的修改是不可變對象呢?
(1) 不可變對象的修改會返回一個新的對象,不用擔心原對象在不小心的情況下修改導致的錯誤,方便程序的管理和調試。
(2) 處于性能的考慮,對象組件的狀態(tài)是不可變對象時,在組件的shouldComponentUpdate方法中僅需要比較前后兩次狀態(tài)對象的引用就可以判斷狀態(tài)是否真的改變,從而避免不必要的render調用。
進階
除了以上方法改變react組件的狀態(tài)之外,我們還經常會用到replaceState()改變組件的狀態(tài)。
replaceState()方法與setState()類似,但是方法只會保留nextState中狀態(tài),原state不在nextState中的狀態(tài)都會被刪除。
//使用語法: replaceState(object nextState[, function callback])
nextState,將要設置的新狀態(tài),該狀態(tài)會替換當前的state。callback,可選參數(shù),回調函數(shù)。該函數(shù)會在replaceState設置成功,且組件重新渲染后調用。
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

