Getting startedReact tutorial

//Nesting data types

Nesting data types

Conflict-free data types often most useful when nested inside each of other, as this allows a parent data type to be edited at the same time as a child, without any conflicts occurring.

On the previous page, we built a collaborative input field—on this page we’ll take the input and transform it into an editable list of fields using LiveList.

Initial state

The first step is to set up your types and initial state. Open liveblocks.config.ts and modify your types so that we have a LiveList of people, instead of a singular person.

Modify the code in /liveblocks.config.ts
// Person typetype Person = LiveObject<{  name: string;  age: number;}>;
// Global typesdeclare global { interface Liveblocks { Storage: { people: LiveList<Person>; }; }}

Then, open App.tsx, and modify the initialStorage value to match your types—we now need a LiveList called people containing a person.

Modify the code in /App.tsx
initialStorage={{  people: new LiveList([    new LiveObject({ name: "Marie", age: 30 })  ]),}}

Great, we’re ready to update our app!

Updating the mutations

Next, let’s modify the mutations—switch to Room.tsx and look at updateName. We need to update this function to take a list index, which we can then use to get the current person with LiveList.get.

Modify the code in /Room.tsx
// Update name mutationconst updateName = useMutation(  ({ storage }, newName: string, index: number) => {    const person = storage.get("people").get(index);    person.set("name", newName);  },  []);

We can then create a mutation for adding a new person to the list. Within this mutation we’re creating a new LiveObject with a default value, before adding it to the list with LiveList.push.

Modify the code in /Room.tsx
// Add person mutationconst addPerson = useMutation(({ storage }) => {  const newPerson = new LiveObject({ name: "Grace", age: 45 });  storage.get("people").push(newPerson);}, []);

We’ll call both of these mutations from the UI.

Rendering the LiveList

To render the list, we first need to update the selector function to return people instead of person.

Modify the code in /Room.tsx
const people = useStorage((root) => root.people);

Because useStorage converts your conflict-free data structures into regular JavaScript structures, people is an array, meaning we can simply map through it in React like any other.

Modify the code in /Room.tsx
return (  <div>    {people.map((person, index) => (      <input        key={index}        type="text"        value={person.name}        onChange={(e) => updateName(e.target.value, index)}      />    ))}  </div>);

Make sure to pass the index to updateName! After adding this, we can then create a button that calls addPerson.

Modify the code in /Room.tsx
return (  <div>    {people.map((person, index) => (      <input        key={index}        type="text"        value={person.name}        onChange={(e) => updateName(e.target.value, index)}      />    ))}    <button onClick={addPerson}>Add</button>  </div>);

We now have nested data structures working!