4种方法,轻松搞懂 React 组件通信

如果你学过 Vue,那一定会知道每个应用都是由众多小组件构成的。拿一台手机来打比方,手机的硬件系统由主板、内存、按键等模块构成,这些不同的模块就属于手机的“组件”。

到了前端领域,组件一般都是各种页面上的按钮、开关、导航栏等元素。一般来说,这些组件之间需要相互传递数据,并产生对应的变化,那么我们在写 React 的时候是怎么实现组件之间通信的呢?(文中代码均采用函数组件+TypeScript 写法,供参考)

组件之间通信的类型

  1. 父组件——子组件
  2. 子组件——父组件
  3. 跨级组件
  4. 兄弟组件

父传子:props和自定义函数事件

父组件可以直接向子组件传递 props,子组件接收到数据后进行更新,再同步给父组件。

function Money() {
    const [selected, setSelected] = useState('food');
    return (
            <TagsSection value={selected}/>
    );
}
复制代码
type Props = {
  //声明props的数据类型
    value: string;
}

const TagsSection: React.FC<Props> = (props) => {
    const tagName = props.value;
    return (
        <div>the tag name is { tagName }</div>
    );
};
复制代码

子传父:props.onchange

props.onchange 相当于子组件发给父组件的一个提醒:我这里的数据变了,你那边也要更新!

const TagsSection: React.FC<Props> = (props) => {
    const onToggleTag = (tagId: number) => {
        const index = selectedTagIds.indexOf(tagId);
        if (index >= 0) {
            props.onChange(selectedTagIds.filter(t => t !== tagId));
        } else {
            props.onChange([...selectedTagIds, tagId]);
        }
    };
    }
复制代码

子组件 TagsSection 内部有个 onToggleTag 函数,判断传入的 tagId 索引是否大于或等于0。如果大于等于0,那么使用 selectedTagIds.filter 方法,然后用 props.onchange 把数据传给父组件,让父组件来改变数据。

注意:如果有需要同步给父组件的数据,请务必使用 props.onchange,否则子组件自己改了数据,父组件都不知道,拿就无法同步了。

跨级组件:Context

一般跨级组件都是一些多组件公用的数据,比如我们登录的用户信息、UI主题等等。如果组件的层级比较深,如果只是用 props ,只能通过一层一层的组件来接力下去,还挺麻烦的。

function App(){
	return (
  	<Toolbar theme="dark">
  )
}

function Toolbar(props) {
  return (
    <div>
      <ThemeButton theme={props.theme} />
    </div>
  );
}

function ThemeButton(){
	return (
  	<Button theme={props.theme} />
  )
}
复制代码

在上面的代码中,子组件 Button 想要拿到 themecolor ,那么就得把 theme 作为 props,从 App 一路传到 ThemeButton

针对这样的情况,React 提供了Context,可以实现多层级的组件穿透。

const ThemeContext = React.createContext('light');

function App() {
    return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
}

function Toolbar() {
  return (
    <div>
      <ThemeButton />
    </div>
  );
}

function ThemeButton() {
  render() {
    return (
      <ThemeContext.Consumer>
      {value=><Button theme={value} />}
  		</ThemeContext.Consumer>
    );
  }
}
复制代码

React.createContext 创建了一个 context 对象,如果某个组件订阅了这个对象,在渲染的时候,会从离这个组件最近的一个 Provider 组件中获取当前 context

Context 的使用方式:

  • 使用 const xxx = createContext(null) 创建上下文
  • 使用 <xxx.Provider> 圈定作用域
  • 在作用域内使用 useContext(xxx) 来使用上下文

同级组件共享数据源

如果父组件中,含有两个及以上的组件之间要进行通信,那么我们可以把数据源存放在它们共同的父组件中。

function Money() {
    const [selected, setSelected] = useState(defaultFormData);
    const onChange = (obj: Partial<Selected>) => {
        setSelected({...selected, ...obj});
    };
    
    return (
                <CategorySection value={selected.category}
                                 onChange={(category) => onChange({category})}/>
            <TagsSection value={selected.tagIds}
                         onChange={(tagIds) => onChange({tagIds})}/>
    );
}
复制代码

案例中有 CategorySectionTagsSection 两个子组件,他们的 value 都来自于父组件 Moneyselected

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享