Let’s talk about toRef, toRefs, and their differences

toRef: Create a new Ref variable and convert a field of the Reactive object into a Ref variable
toRefs: Create a new object, each of its fields is the Ref variable of each field of the Reactive object

Tell me about toRef
First define a reactive object

interface Member {<!-- -->
  id: number
  name: string
}
const userInfo: Member = reactive({<!-- -->
  id: 1,
  name: 'White'
})
console.log('userInfo=', userInfo)

If you want to convert the name field of the userInfo object into a Ref variable, you can do the following: let nameR = toRef(userInfo, 'name')
At this time, this nameR is a Ref variable,
Therefore, when reading and assigning values later, you must use nameR.value to operate.
Therefore, when reassigning nameR, the values of nameR and userInfo.name will be updated at the same time.

let nameR = toRef(userInfo, 'name')
console.log('nameR is a Ref variable, value=', nameR.value)

/**
* @name editNameR
* @description Modify the value of nameR
*/
const editNameR = () => {<!-- -->
  nameR.value = 'edit-White'
  // You can see that after reassigning `nameR`, the values of `nameR` and `userInfo.name` are updated at the same time
  console.log('edit - nameR=', nameR.value)
  console.log('edit - userInfo=', userInfo)
}

toRef can also receive an array. In this case, the second parameter is the subscript of the array.

let wordList = reactive(['a', 'b', 'c'])
let a = toRef(wordList, 0)
console.log('a=', a.value) // a
console.log('wordList[0]=', wordList[0]) // a

Object – Set default value
If there is a property on the Reactive object that does not have an initial value, you can pass the third parameter to set it (the default value is only valid for Ref variables)

interface MemberCopy {<!-- -->
  id: number
  name: string
  age?: number // The age attribute, because it is optional, the default value will be `undefined`
}
// When declaring variables, omit the `age` attribute
const theInfo: MemberCopy = reactive({<!-- -->
  id: 1,
  name: 'Black'
})

// At this time, in order to avoid program running errors, you can specify an initial value, but the initial value is only valid for the Ref variable and will not affect the value of the Reactive field.
let age = toRef(theInfo, 'age', 18)
console.log('age=', age.value) // age= 18
console.log('theInfo.age=', theInfo.age) // theInfo.age= undefined

// Unless reassigned, both will be updated at the same time
age.value = 25
console.log(age.value) // 25
console.log(theInfo.age) // 25

Array – Set default value

const words = reactive(['a', 'b', 'c'])

// When the value corresponding to the subscript does not exist, return `undefined`
const d = toRef(words, 3)
console.log(d.value) // undefined
console.log(words[3]) // undefined

// After setting the default value, the default value will be used for the Ref variable. The Reactive array will not be affected at this time.
const e = toRef(words, 4, 'e')
console.log(e.value) // e
console.log(words[4]) // undefined

There is also a special usage that is not recommended,
In the process of toRef, if a key that does not exist on the original object is used, the .value of the defined Ref variable will be undefined code>

Give an example
// As we all know, White does not have a girlfriend
const girlfriend = toRef(userInfo, 'girl')
console.log('girlfriend=', girlfriend.value) // girlfriend= undefined
console.log('userInfo.girlfriend=', userInfo.girl) // userInfo.girl= undefined
//At this time, there are only two Keys on the userInfo object
console.log(Object.keys(userInfo)) // ['id', 'name']

/*
  If, assign a value to the Ref variable (girlfriend) of this non-existent key,
  Then the original `Reactive object (userInfo)` will also add the key (girl) synchronously, and its value will also be updated synchronously.
*/
girlfriend.value = 'Marry'
console.log('girlfriend=', girlfriend.value) // girlfriend= Marry
console.log('userInfo.girl=', userInfo.girl) // userInfo.girlfriend= Marry
console.log('Look at the attributes of userInfo =', userInfo) // Girl, id, name of Proxy

Why does emphasize not to use in TypeScript? Because at compile time, TypeScript’s type checking cannot be passed.
If you must use it, you can consider using the any type, as follows: 1, 2, 3

// 1. Directly specify the type as `any`
type Member = any
//Of course it is usually `const userInfo: any`

// 2. Or allow any key value while maintaining the interface type.
interface Member {<!-- -->
  [key: string]: any
}

// 3. The same applies to using `Record`
type Member = Record<string, any>

Tell me about toRefs:
Unlike toRef, toRefs only accepts one parameter (a reactive variable)

interface People {<!-- -->
  id: number
  name: string
}

// Declare a `Reactive` variable. At this time, the TS type of `theKing` is: const theKing: People
const theKing: People = reactive({<!-- -->
  id: 1,
  name: 'Black'
})
console.log('theKing=', theKing)

// Pass `toRefs` as input parameter. At this time, the TS type of this new `useToRefs` variable is no longer `People`, but: const useToRefs: ToRefs<People>
const useToRefs = toRefs(theKing)
console.log('useToRefs=', useToRefs)

// You can also rewrite a new type to specify it, because each field is a Ref variable associated with the original, so it can also be declared like this:
interface newPeople {<!-- -->
  id: Ref<number>
  name: Ref<string>
}
const useToRefsCopy: newPeople = toRefs(theKing)
console.log('useToRefsCopy=', useToRefsCopy)

That’s all. In fact, you don’t need to manually specify the type in daily use. TypeScript will automatically derive it, which can save a lot of development work.

Convert an array

const charList = reactive(['a', 'b', 'c'])
const charListRefs = toRefs(charList)
console.log('charListRefs=', charListRefs)
console.log('charListRefs[0]=', charListRefs[0].value) // charListRefs[0]= a

Destructuring and assignment,
This is very different from directly deconstructing a Reactive variable. When deconstructing a Reactive variable directly, you get an ordinary variable that no longer has any response.

// The `Reactive object or array` converted with `toRefs` supports ES6 destructuring and `will not lose responsiveness`, because every variable after destructuring is responsive.
const {<!-- --> name } = toRefs(theKing)
console.log('name=', name.value) // name= Black

// At this time, reassign the deconstructed variables and the original variables will be updated simultaneously.
name.value = 'Tom'
console.log('After reassignment-name=', name.value) // After reassignment-name= Tom
console.log('After reassignment-theKing', theKing.name) // After reassignment-theKing Tom


//--------------------------------Look at the example below---------------- ----------

/*
  Take a calculator function as an example,
  This time, it is modified to have a Reactive data status center inside, which is deconstructed into multiple Ref variables when the function returns.
  In this way, when calling the useCalculator function, the Ref variable can be obtained directly through destructuring, without the need for additional conversion work.
*/
interface CalculatorState {<!-- -->
  num: number,
  step: number // The amount to be increased for each calculation
}
// Declare a function that "uses the calculator"
const useCalculator = () => {<!-- -->
  // Manage internal variables in the form of data center
  const state: CalculatorState = reactive({<!-- -->
    num: 0,
    step: 10
  })

  const add = () => {<!-- -->
    state.num + = state.step
  }

  return {<!-- -->
    ...toRefs(state),
    add
  }
}

//The deconstructed `num` and `step` are both Ref variables
const {<!-- --> num, step, add } = useCalculator()
console.log('num=', num.value) // num= 0
console.log('step=', step.value) // step= 10
// Call the calculator method, and the data will also be updated responsively
add()
console.log('After calling the add() method, num=', num.value) // After calling the add() method, num= 10