You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React-Select触发父组件重渲染:实现仅子组件重渲染

Fix Unnecessary Parent Re-renders with Formik + React-Select

Let’s tackle this re-rendering issue step by step—you’re right that the deep component tree re-renders are hurting performance, and they’re directly tied to your closeMenuOnSelect={false} problem too. Here’s how to lock re-renders down to just your React-Select component:

Why You’re Seeing This

The root causes are:

  • Formik’s render prop creates a new function every time your form state shifts, forcing the entire subtree (including parent components) to re-render.
  • Your dependencies prop is passed up the tree, and if it’s a new object/array reference on each render (common if created in a parent component), it triggers re-renders all the way up.
  • Conversion functions like convertNamespaceToDefaultValue and convertNamespaceToSelect generate new objects/arrays on every render, making React think props have changed even when they haven’t.

Step-by-Step Fixes

1. Extract React-Select into a Memoized Component

Wrap your Select in React.memo to prevent unnecessary re-renders when props haven’t actually changed. This stops Formik’s render prop from creating a new component instance every time:

import { memo } from 'react';
import Select from 'react-select';

const MemoizedNamespaceSelect = memo(({
  defaultValue,
  options,
  onChange,
  id,
  isMulti,
  closeMenuOnSelect
}) => (
  <Select
    isMulti={isMulti}
    closeMenuOnSelect={closeMenuOnSelect}
    id={id}
    defaultValue={defaultValue}
    options={options}
    onChange={onChange}
  />
));

2. Cache Conversion Results with useMemo

Use React’s useMemo to cache the output of your conversion functions. This ensures defaultValue and options passed to your memoized select don’t get new references on every render:

3. Optimize Formik Field Logic

Keep the render prop but wrap inner handlers in useCallback to stabilize their references, or pass your memoized component directly to Formik’s Field via the component prop. Here’s the updated full code:

import { useMemo, useCallback } from 'react';
import { Field, ErrorMessage } from 'formik';

// Include the MemoizedNamespaceSelect from above here

const YourFormSection = ({ dependencies, keyField, index }) => {
  // Cache converted options to avoid new array references
  const selectOptions = useMemo(() => {
    return convertNamespaceToSelect(dependencies.namespacePermissions);
  }, [dependencies.namespacePermissions]);

  // Cache the default value converter function
  const getDefaultValue = useMemo(() => {
    return (fieldValue) => convertNamespaceToDefaultValue(dependencies.namespacePermissions, fieldValue);
  }, [dependencies.namespacePermissions]);

  return (
    <div className="w-50">
      <Field
        name={`${keyField}.${index}.permissions`}
        render={({ field: { value, name }, form: { setFieldValue, setFieldTouched } }) => {
          // Stabilize onChange handler reference
          const handleChange = useCallback((values) => {
            setFieldValue(name, convertSelectToNamespacesData(values));
            setFieldTouched(name, true);
          }, [name, setFieldValue, setFieldTouched]);

          return (
            <div>
              <label htmlFor="namespace-permissions" className="font-weight-medium">
                Permissions in Namespace <Asterisk />
              </label>
              <MemoizedNamespaceSelect
                id="namespace-permissions"
                isMulti
                closeMenuOnSelect={false}
                defaultValue={getDefaultValue(value)}
                options={selectOptions}
                onChange={handleChange}
              />
              <ErrorMessage name={name} component={FormErrorMessage} />
            </div>
          );
        }}
      />
    </div>
  );
};

4. Fix Parent Component Re-renders

If parents are still re-rendering, check if dependencies is being recreated in the parent. Wrap it in useMemo there too:

// In the parent component passing dependencies
const dependencies = useMemo(() => {
  return {
    namespacePermissions: /* your source data here */
  };
}, [/* dependencies for your namespacePermissions data */]);

Why This Works

  • React.memo does a shallow prop comparison for your Select—only re-renders if props actually change.
  • useMemo ensures options and default values keep stable references unless the underlying namespacePermissions data changes.
  • useCallback keeps your onChange handler’s reference stable, so it doesn’t trigger unnecessary re-renders in the memoized Select.

Once the re-rendering issue is fixed, your closeMenuOnSelect={false} should work as expected too—since the menu won’t get unmounted/re-mounted on every selection.

内容的提问来源于stack exchange,提问作者user10104341

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:32:17