React.Children.map的用法

发布时间 2023-11-15 20:14:35作者: shellon

React.Children用很多用法,如下图,经常会用到的是toArray(),具体用法可以自行了解,这里记录下map()的用法和使用到的场景。

1. 用法:React.Children.map接收2个参数,第一个是所有子元素,第二个是个回调,可以对每个子元素进行处理,然后返回处理后的子元素。
2. 使用场景:子元素(也可理解为子组件)有相同的处理逻辑或者数据(脑里突然闪出useContext和useReducer,可以尝试看看);子元素是不确定的,无法使用传统的父子组件(准确说,这里的子元素是个页面,有七八个页面需要增加相同的业务需求,这个做法有点类似网站的顶部导航这类公共布局,因此第一个想法就是定义一个Layout,使用React.Children.toArray,然后在需要处理的页面上最外层加上<Layout>...</Layout>,但是这样没法对子元素页面进行操作,所以采用React.Children.map来实现子元素页面想要的功能(这里用到React.cloneElement)。脑里又闪出新的想法,将这七八个页面路由入口都放在Layout上,通过路径决定子元素使用哪个页面组件,这样就是传统的父子组件传递数据,或许更容易理解)。
3. 实现代码:
// Layout.tsx
interface LayoutProps {
  children: React.ReactElement | React.ReactElement[];
}

function Layout(props: LayoutProps) {
  const location = useLocation();
  const pathname = location.pathname;

  const sessionKey = `Filter_${pathname}`;
  // 获取缓存的数据
  const searchStr = sessionStorage.getItem(sessionKey);
  // 即用即销
  sessionStorage.removeItem(sessionKey);
  // 这里想要实现,点击前往其他页面前,将本页面的搜索条件缓存,便于下次回到本页面时使用
  const handleClick = (searchVal: SearchValProps, id: string) => {
    if (searchVal && Object.keys(searchVal).length > 0) {
      const searchStr = JSON.stringify(searchVal);
      sessionStorage.setItem(sessionKey, searchStr);
    }
    window.location.href = linkUrl(jobId);
  };
  return (
    <div className="template-layout">
      {React.Children.map(props.children, (child: React.ReactElement) =>
        // cloneElement第二个参数将作为child的props传入
        React.cloneElement(child, {
          searchVal: searchStr ? JSON.parse(searchStr) : undefined,
          onClickJobCard: handleClick,
        })
      )}
    </div>
  );
}

export default Layout;
// Page1.tsx
function Index(props: any) {
  const {
    searchVal,
    onClickJobCard,
    ...otherProps
  } = props;
  const [filterVals, setFilterVals] = useState<any>({});
    
  useEffect(() => {
    setFilterVals(searchVal);
  }, [searchVal]);
  
  const handleClick = (jobId: string, prefix: string) => {
    onClickJobCard(filterVals, jobId, prefix);
  };
  
  return (
    <div>
      // 这里简单写了,意思到位即可
      <Search value={filterVals} ... />
      <Card onClick={handleClick} ... />
    </div>
}

function Page1(props: any) {
  // 这里的props是原来的props,如果想在原子组件Index中使用,需要作为属性再次传入
  return (
    <JobLayout>
      <Index {...props} />
    </JobLayout>
  );
}

export default Page1;