4

until now in my code, I had an object that would represent all the data for an image on the page

this.state = {
    img-1: {
        x: 0,
        y: 0,
        rotation: 0
    },
    img-2: {
        x: 20,
        y: 200,
        rotation: 50
    }
}

every time the object receives a new child it adds a new img-id to state that is updated everytime <img id=${id} update={this.update} /> is updated.

Moving functionality like calculating the coordinates or the rotation into their own custom hooks would greatly improve my code in terms of maintainability and testing but I don't really see a good way to store all of this data in a centralized object with hooks.

As far as I understand it I'd have to set either a new

[img-1, setImg-1] = useState({ x: 0, y:0, rotation: 0 })

for every child which, as I understand it, isn't possible as hooks have to be declared at the top level or to set a very deep object that would be kind of clunky to update:

[images, setImages] = useState({
    img-1: {
        x: 0,
        y: 0,
        rotation: 0
    }
})

const createImg = (newImg) => { setImages({...images, newImg}) }

const updateImg = (id, updatedImg) => {
    setImages({ ...images, [`img-${id}`]{...updatedImg} }
)}

Is there a cleaner / more readable approach or do I just have to resort to nesting everything in one object?

3
  • Why is it clunky to update? It's pretty close to what you would do with setState. Commented Feb 20, 2019 at 10:38
  • 1
    enumerated properties are awful, better use an array. And - is no valid character in a variable-name. [img-1, setImg-1] = ... Commented Feb 21, 2019 at 6:53
  • those will be actual id's later on. What's the problem with the -? ist it just convention not to use it or does it cause problems? Commented Feb 21, 2019 at 9:28

1 Answer 1

8

Instead of using a useState, you can make use of useReducer and control your states better and handle dynamic addition of states.

const initialState = {
  'img-1': {
    x: 0,
    y: 0,
    rotation: 0,
  },
};

const reducer = (state, action) => {
  switch (action.type) {
    case 'ADD_IMAGE':
      return {
        ...state,
        [action.itemkey]: action.payload,
      };
    case 'UPDATE_IMAGE':
      return {
        ...state,
        [action.id]: { ...state[action.id], ...action.payload },
      };
    default: {
      return state;
    }
  }
};

In case you're using a functional component, the code then looks as follows.

const [state, dispatch] = useReducer(reducer, initialState);

const createImg = (newImg) => {
  dispatch({
    type: 'ADD_IMAGE',
    payload: { newImg },
    itemKey: `item-${Object.keys(state).length + 1}`,
  });
};

const updateImg = (id, updatedImg) => {
  dispatch({ type: 'UPDATE_IMAGE', id, payload: updatedImg });
};
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.