对⼀个问题的研究,尝试⽤⻩⾦圈法则 来分析。

  • Why 为什么 React 需要 immutable
  • How 如何在使⽤ React 使⽤ immutable
  • What 带来的收益和结果

为什么要在React 使⽤ immutable

image-20210322191536116

⼀句话概况: React 使⽤了 shallowCompare 来决定是否应该重新渲染⼀个组件。

要理解 shallowCompare,我们⾸先需要知道 JavaScript 是如何处理对象引⽤的。让我们来看 ⼀个例⼦:

const people = [{ name: "mary", age: 20 }, { name: "john", age: 30 }];
const john = people[1];
const jonhClone = { name: "john", age: 30 };
console.log(john === people[1]) // true
console.log(john === jonhClone) // false
people[1].age = 31;
console.log(john) // { name: "john", age: 31 }
console.log(jonhClone) // { name: "john", age: 30 }

johnClonenameagejohn 相同,但他们不是同⼀个⼈。 当我们声明 john = people[1], 我们没有拷⻉ people[1]nameagejohn ,两个变量都指向同⼀个对象作为引⽤。

image-20210322191812023

image-20210322191830084

ShallowCompare

现在我们来看⼀个 shallowCompare的例⼦:

let oldProps = null;
function shallowCompare (props){
 if(props !== oldProps) console.log(props);
 oldProps = props;
}
const mary = { name: "mary", age: 20 };
const john = { name: "john", age: 30 };
shallowCompare(mary) // prints -> { name: "mary", age: 20 }
shallowCompare(john) // prints -> { name: "john", age: 30 }
john.age = 31;
shallowCompare(john) // will not print!

如果我们想打印 john 的更新,我们需要浅拷⻉它。这样⼀来,shallowCompare 就会知道道具发⽣了变化。

let oldProps = null;
function shallowCompare (props){
 if(props !== oldProps) console.log(props);
}
const john = { name: "john", age: 30 };
shallowCompare(john) // prints -> { name: "john", age: 30 }
const johnClone = {...john};
johnClone.age=31;
shallowCompare(johnClone) // prints -> { name: "john", age: 31 }

React 中的例⼦

React 使⽤同样的原则来避免不必要的组件重渲染。

class People extends Component {
 constructor(props) {
   super(props);
   this.state = {
   	people:[{ name: "mary", age: 25 }, { name: "john", age: 30 }]
   };
 }
 updateJohnAge = ()=>{
   const people = [...this.state.people];
   people[1].age=31;
   this.setState({people});
   //This will not work.
   //People is a new array, but john is the same object
 }
 render() {
   return (
     <div>
       <Person person={this.state.people[1]}></Person>
       <button onClick={this.updateJohnAge}>Set john age to 31</button>
     </div>
   );
 }
}
class Person extends Component {
 render() {
   const { person } = this.props;
   return (
     <div>
       <p>name: {person.name}</p>
       <p>age: {person.age}</p>
     </div>
    );
  }
}

image-20210322192007188

如果我们点击按钮,组件将不会呈现 john 的更新,因为 people[1] 是上次的同⼀个引⽤。为此,没有改变 john 。我们需要在之前克隆 john ,然后更新数组。

updateJohnAge = () =>{
 const people = [...this.state.people];
 const johnClone = {...people[1]};
 johnClone.age = 31;
 people[1] = johnClone;
 this.setState({people});
 //This will work.
}

image-20210322192045811

要知道,我们并没有拷⻉ people[0] (mary)Immutable 并不意味着我们要详尽地克隆所有对象的 ramifications。这将不会很有效率。我们只需要通过我们改变的路径来创建新的引⽤。

Q.E.D.


一个热心肠的正经前端程序猿~