关于程序员:React-state-结构设计原则

3次阅读

共计 3976 个字符,预计需要花费 10 分钟才能阅读完成。

React State 结构设计

React 中组件 state 状态治理是组件设计中的难点之一,如何设计 state 的构造。遵循以下准则能够保障 state 更新不呈现逻辑上的谬误,也能够防止不必要的 state 保护:

  • 相干的状态组合成一个 group。当每次触发更新的时候须要更新两个 state 则这两个 state 能够尝试合并成一个 state【从单个值类型,变成 object 或者 Array 等类型】。
import {useState} from 'react';

function ComA() {
  // bad case 
  const [x, setX] = useState<number>(0);
  const [y, setY] = useState<number>(0);
  
  // good case
  const [position, setPosition] = useState({x: 0, y: 0});
  return (
    <div>
      <div style={{
          position: 'absolute',
          backgroundColor: 'red',
          borderRadius: '50%',
          transform: `translate(${x}px, ${y}px)`,
          left: -10,
          top: -10,
          width: 20,
          height: 20,
        }} />
    </div>
    );
}
  • 避免出现竞态的 state,也就是说两个或多个 state 存在竞态,同一时刻有且仅有一个是真值。如果存在这种问题,则须要思考防止以后这种 state 的构造, 应用不同的值去辨别抵触的 state,这样就把多个抵触的 state 合并成 1 个 state,区别在于 value 的变动以及其代表的意义。
import {useState} from 'react';

// bad  case
function ComA() {
  // 示意编辑状态
  const [isWritting, setIsWritting] = useState(true);
  // 示意是否保留
  const [isSave, setIsSave] = useState(fasle);
  
  // 其余状态
  const [isComplete, setIsComplete] = useState(false);
  
  return (//...);
}

// 这两头 isWriting 和 isSave 是抵触的。也就是说两个 state 存在竞态,有且仅有一个是真值。// combine mutilate state ingroup 
function ComB() {const [status, setStatus] = useState<'writing' | 'save' | 'complete'>('writing');
  
  return (// ...);
}
  • 防止多余的 state。如果一个 state 能够通过其余 state 的计算得出【.length, 取反异或等】,那么这个 state 就是不须要存在的。
export default function Form() {const [firstName, setFirstName] = useState('');
  const [lastName, setLastName] = useState('');
  const [fullName, setFullName] = useState('');

  function handleFirstNameChange(e) {setFirstName(e.target.value);
    setFullName(e.target.value + ' ' + lastName);
  }

  function handleLastNameChange(e) {setLastName(e.target.value);
    setFullName(firstName + ' ' + e.target.value);
  }

  return (
    <>
      <h2>Let’s check you in</h2>
      <label>
        First name:{' '}
        <input
          value={firstName}
          onChange={handleFirstNameChange}
        />
      </label>
      <label>
        Last name:{' '}
        <input
          value={lastName}
          onChange={handleLastNameChange}
        />
      </label>
      <p>
        Your ticket will be issued to: <b>{fullName}</b>
      </p>
    </>
  );
}


// fullname  齐全能够由 firstName 和 lastName 拼接进去,应用独自的 state 来保留计算结果是多余的。
  • 防止反复的状态。如果 state 存在反复雷同的数据时,这部分反复的数据很难放弃同步更新。【个别是针对数组项的解决,data 保留在一个 state 中,而后又应用一个 state 保留选中或者编辑某项。这时候 data 中的数据更新,current 可能会被缓存到旧值】。须要防止这种反复。解决办法【防止保留反复的内容,而是保留找到指定数据的 id 或者索引】。
import {useState} from 'react';

const defaultData = [{ title: 'Tom', id: 0},
    {title: 'Sam', id: 1},
    {title: 'Dodo', id: 2},
    {title: 'Piker', id: 3},
];
function ComA() {const [data, setData] = useState(defaultData);
    const [current, setCurrent] = useState(data[0]);
  
    function handleClick(item) {setCurrent(item);
    }
  
    function handleInput(id, value) {const newData = data.map((item)=>{if (id === item.id) {return { ...item, title: value};
        }
        return item;
      })
    }
  
  return (
    <>
      <ul>
        {data.map((item) => {
            return (<li key={item.id}>
                <input value={item.title} onChange= {({target})=>{handleInput(item.id, target.value); }}/>
                <button onClick={()=>{ handleClick(item) }}> 选中 </button>
              </li>
            );
          })
        }
      </ul>
      <p> 以后选中:{current.title}</p>
     </>
  );
}


// 问题:当点击“选中”按钮后,current 保留了以后 item 的一个援用。接着编辑以后项的 title,发现并不会同步到 <p> 中展现。

解决办法 1
仔细查看代码能看进去,通过 handleInput 执行时,返回了新的对象更新 data 中的 item。只有略微批改一下 handleInput 的代码,同时更新 current 即可。

function handleInput(id, value) {const newData = data.map((item)=>{if (id === item.id) {// return { ...item, title: value};
          const newItem = {...item, title: value};
          setCurrent(newItem);
          return newItem;
        }
        return item;
      })
    }

然而这种形式不能一劳永逸,其余函数中再批改其余属性数据,还得减少同样的逻辑。

解决办法 2 :保留 item 的 id 不要保留反复的数据内容。

import {useState} from 'react';

const defaultData = [{ title: 'Tom', id: 0},
    {title: 'Sam', id: 1},
    {title: 'Dodo', id: 2},
    {title: 'Piker', id: 3},
];
function ComA() {const [data, setData] = useState(defaultData);
    // 批改
    const [currentId, setCurrentId] = useState(0);
  
    const currentItem = data.find(({id}) => id === currentId);
  
    function handleClick({id}) {setCurrentId(id);
    }
  
    function handleInput(id, value) {const newData = data.map((item)=>{if (id === item.id) {return { ...item, title: value};
        }
        return item;
      })
    }
  
  return (
    <>
      <ul>
        {data.map((item) => {
            return (<li key={item.id}>
                <input value={item.title} onChange= {({target})=>{handleInput(item.id, target.value); }}/>
                <button onClick={()=>{ handleClick(item) }}> 选中 </button>
              </li>
            );
          })
        }
      </ul>
      <p> 以后选中:{current.title}</p>
     </>
  );
}

一劳永逸解决问题,保留 id。更新的时候组件会主动获取对应的数据项

  • 避免出现过深的嵌套 state。深度嵌套的 state 不便于更新,更新时,须要一层一层的解构,重组成新的嵌套对象。如果能够尝试应用平铺的形式组织 state 构造。react 进行 state 更新时,援用类型数据须要应用新的援用构造进行更新【解构复制,批改对应 value】,如果嵌套层级过多,更新时解构层级越简单,容易出问题。

以这些准则作为 state 结构设计方法论,逐渐实现性感 & 正当的 React 组件!

本文由 mdnice 多平台公布

正文完
 0