New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details
Socket
Book a DemoSign in
Socket

use-prop

Package Overview
Dependencies
Maintainers
1
Versions
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

use-prop

Updating State From Properties With React Hooks

latest
Source
npmnpm
Version
1.0.4
Version published
Weekly downloads
0
Maintainers
1
Weekly downloads
 
Created
Source

介绍

这是一个解决外部 Props 更新到组件内部的 State 的方案,优雅的解决了 State 依赖外部 Props 并需要手动监听的问题。

安装

use-prop 以 NPM 包的形式提供。

# 使用 npm
npm install use-prop

# 使用 yarn
yarn add use-prop

使用

import React from 'react'
import useProp from 'use-prop'

const App = (props) => {
  const { user: userProp } = props
  const [user, setUser] = useProp(userProp)
  return (
    <>
      <p>Name: {user.name}</p>
      <input
        type="text"
        placeholder="Enter Your Name"
        onKeyPress={(event) => {
          if (event.code === "Enter") {
            setUser((u) => ({ ...u, name: event.currentTarget.value }))
          }
        }}
      />
    <>
  )
}

export default App

为什么选择 use-prop?

在您的项目中,是否有很多将 Props 或 Context 的更新同步到 State 的情况?

我们有多种方法处理这种情况,use-prop 为以下方案的第三种:

  • 使用 useEffect 监听变化;
  • 使用 useRef 跟踪上一个值的变化;
  • 封装自定义 Hooks;

使用 useEffect( ❌ 最好不要)

您是不是经常在项目中看到这样的用法?

import { useState, useRef, useEffect } from "react";

const Users = ({ users }) => {
  const [internalUsers, setInternalUsers] = useState(users);

  useEffect(() => {
    setInternalUsers(users);
  }, [users]);

  return (
    <ul>
      {internalUsers.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
};

这几行代码看起来非常的优雅:使用 useEffect 的第二个参数监听 users 的变化,然后重新设置 state,完美!✌️

可是您有没有想过,上面的代码会额外的触发了一次重渲染。🤔️

每当 users 变化时,直到整个应用加载完 DOM 后,useEffect 里的函数才会执行。由于我们调用了 setInternalUsers,这又会导致我们的程序额外的重渲染一次。

为了给用户提供一个加载更快、体验更好的应用程序,请不要这样做。

使用 useRef( ✅ )

import { useState, useRef, useEffect } from "react";

const Users = ({ users }) => {
  const [internalUsers, setInternalUsers] = useState(users);
  const previousUsersRef = useRef();

  if (users !== previousUsersRef.current && users !== internalUsers) {
    setInternalUsers(users);
  }

  useEffect(() => {
    previousUsersRef.current = users;
  }, [users]);

  return (
    <ul>
      {internalUsers.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
};

此写法用到了 useRef 来保存上一次更新的值,然后在渲染阶段判断更新前和更新后的值是否相同,如果不相同则设置 state。

从而在渲染前的阶段就完成了 state 的设置,避免了额外的渲染。

虽然比上一个写法多出了几行代码,但是在性能、用户体验(屏幕闪烁)上都优于第一种写法。

自定义 Hooks ( ✅ ✅ )

第二种写法虽然解决了我们的问题,但是其可维护性较低、耦合度较高。

这些逻辑全部写在了组件内部,后来的维护者们有可能因为修改其他问题,而使你写的功能失效或出现其他小问题;

或者您的组件不止监听一个 Props;

又或者其他组件也需要此功能;

对此,您应该将此逻辑抽离成自定义 Hooks。

// File: useProp.ts
import { useState, useRef, useEffect } from "react";

type StateListeningPropResult<T> = [T, React.Dispatch<React.SetStateAction<T>>];

const useProp = <T>(prop: T): StateListeningPropResult<T> => {
  const [state, setState] = useState<T>(prop);
  const previousPropRef = useRef<T>();

  if (prop !== previousPropRef.current && prop !== state) {
    setState(prop);
  }

  useEffect(() => {
    previousPropRef.current = prop;
  }, [prop]);

  return [state, setState];
};

export default useProp;

像这样,就能在您的组件中使用了。

// File: Users.tsx
import React from "react";
import useProp from "./useProp";

type User = { id: string; name: string };

const Users: React.FC<{ users: User[] }> = (props) => {
  const { users: usersProp } = props;

  const [users, setUsers] = useProp(usersProp);

  const addUser = (userName: User["name"]) => {
    setUsers((oldUsers) =>
      oldUsers.concat({
        id: Date.now().toString(),
        name: userName,
      })
    );
  };

  const handleKeyPress = (event: React.KeyboardEvent<HTMLInputElement>) => {
    if (event.code === "Enter") {
      addUser(event.currentTarget.value);
      event.currentTarget.value = "";
    }
  };

  return (
    <div>
      <input
        type="text"
        placeholder="Enter Your Name"
        onKeyPress={handleKeyPress}
      />
      <ul>
        {users.map((user) => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
};

Keywords

React

FAQs

Package last updated on 12 Oct 2022

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts