如何在React Hooks中动态创建状态对象?

4

到目前为止,在我的代码中,我有一个对象来表示页面上一张图片的所有数据。

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

每次对象接收到一个新的子元素时,它都会向状态添加一个新的img-id,每次更新<img id=${id} update={this.update} />时都会更新该状态。
将计算坐标或旋转等功能移动到自定义钩子中,可以大大提高代码的可维护性和测试性,但我并没有看到一种好的方法来使用钩子将所有这些数据存储在集中式对象中。
据我所知,我必须设置一个新的
[img-1, setImg-1] = useState({ x: 0, y:0, rotation: 0 })

对于每个孩子,据我了解,这是不可能的,因为钩子必须在顶层声明或设置一个非常深的对象,这样更新起来会有些笨拙:

[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} }
)}

有更简洁/易读的方法吗?还是我只能把所有内容嵌套在一个对象中?

为什么更新有些笨重?这与使用setState的操作相当接近。 - Estus Flask
1
枚举属性很糟糕,最好使用数组。而“-”不是变量名中的有效字符。 [img-1, setImg-1] = ... - Thomas
那个横杠有什么问题吗?不使用它只是惯例还是会引起问题? - cubefox
1个回答

8

你可以使用useReducer来代替useState,更好地控制状态并处理动态添加的状态。

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;
    }
  }
};

如果您正在使用函数式组件,代码如下所示。
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 });
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接