react函数式组件通信方式

5,643 阅读4分钟

react函数式组件通信方式

父子组件通信

父传子

    主要通过属性传递的方式:

// App.js(父组件)
import React, { useState } from "react";
import Child from "./Child.js";

export default function App() {
  const [name, setName] = useState("");
  const [list, setList] = useState([]);
  const addList = () => {
    if (name !== "") {
      setList((preList) => {
        const newList = [...preList];
        newList.push({ id: preList.length, name });
        return newList;
      });
    }
  };
  return (
    <div className="App">
      <input
        placeholder="请输入名字"
        value={name}
        onChange={(e) => setName(e.target.value)}
      />
      <button onClick={addList}>添加</button>
      <Child list={list} />
    </div>
  );
}


// Child.js(子组件)
import React from "react";

export default function Child(props) {
  const { list } = props;
  return (
    <ul>
      {list.map((item) => {
        const { id, name } = item;
        return <li key={id}>{name}</li>;
      })}
    </ul>
  );
}

    子组件Child.js通过props接收父组件App.js传递过来的list属性,在页面上输入名字,点点击添加即可添加一行,具体请看例子父子组件传值(父传子)

子传父

    通过调用父组件传递过来的方法,调用方法将参数传递出去,下面的代码在上面父传子代码的基础上做了拓展,添加了删除某一行的功能。

// App.js(父组件)
import React, { useState } from "react";
import Child from "./Child.js";

export default function App() {
  const [name, setName] = useState("");
  const [list, setList] = useState([]);
  const addList = () => {
    if (name !== "") {
      setList((preList) => {
        const newList = [...preList];
        newList.push(name);
        return newList;
      });
    }
  };
  const deleteList = (deleteIndex) => {
    const newList = list.filter((_, index) => index !== deleteIndex);
    setList(newList);
  };
  return (
    <div className="App">
      <input
        placeholder="请输入名字"
        value={name}
        onChange={(e) => setName(e.target.value)}
      />
      <button onClick={addList}>添加</button>
      <Child list={list} deleteList={deleteList} />
    </div>
  );
}

// Child.js(子组件)
import React from "react";

export default function Child(props) {
  const { list, deleteList } = props;
  return (
    <ul>
      {list.map((name, index) => {
        return (
          <li key={index}>
            {name}
            <span
              style={{
                marginLeft: "30px",
                textDecoration: "underline",
                cursor: "pointer"
              }}
              onClick={() => {
                deleteList(index);
              }}
            >
              删除
            </span>
          </li>
        );
      })}
    </ul>
  );
}

    子组件调用父组件传递过来的deleteList方法,并且在调用时将索引index传递给父组件,从而完成删除某一行的功能,具体请看例子父子组件传值(子传父)

跨级组件

    对于跨级组件来说,可以采用层层传递属性的方式来实现组件之间的通信,但是当层级嵌套关系较深的时候,层层传递是比较繁琐且容易出错的,所以这里利用context来实现组件通信。     组件层级关系App>A>B,App中调用了A组件,A组件调用了B组件。

// App.js
import React, { useState, useReducer } from "react";
import listContext from "./listContext";
import A from "./A";

const listReducer = (state, action) => {
  const { type, payload } = action;
  const { list } = state;
  switch (type) {
    case "addList":
      const { name } = payload;
      // 注意这里不能写list.push(name)  永远只能返回新的state,不能改变原来的state
      return { ...state, list: [...list, name] };
    case "deleteList":
      const { deleteIndex } = payload;
      const newList = list.filter((_, index) => index !== deleteIndex);
      return { ...state, list: newList };
    default:
      return state;
  }
};

export default function App() {
  const [name, setName] = useState("");
  const [state, dispatch] = useReducer(listReducer, { list: [] });
  const addList = () => {
    if (name !== "") {
      dispatch({
        type: "addList",
        payload: {
          name
        }
      });
    }
  };
  return (
    <div className="App">
      <input
        placeholder="请输入名字"
        value={name}
        onChange={(e) => setName(e.target.value)}
      />
      <button onClick={addList}>添加</button>
      <listContext.Provider value={{ state, dispatch }}>
        <A />
      </listContext.Provider>
    </div>
  );
}

// A.js
import React from "react";
import B from "./B";

export default function A() {
  return <B />;
}


// B.js
import React, { useContext } from "react";
import listContext from "./listContext";

export default function B() {
  const { state, dispatch } = useContext(listContext);
  const { list } = state;
  const deleteList = (index) => {
    dispatch({ type: "deleteList", payload: { deleteIndex: index } });
  };
  return (
    <ul>
      {list.map((item, index) => (
        <li
          key={index}
          onClick={() => {
            deleteList(index);
          }}
        >
          {item}
        </li>
      ))}
    </ul>
  );
}

// listContext.js
import { createContext } from "react";
const listContext = createContext();
export default listContext;


    上面代码中,在listContext.js中创建了一个关于list的context;在App.js中通过listContext.Provider能够将value值传递给所有被其包裹的组件。具体请看例子跨多层组件传值(context)

兄弟组件

    react的数据流是自顶向下的,无法通过react直接进行兄弟组件通信,通常是子组件向父组件传递数据,再有父组件告知另一个子组件。     组件层级关系App>Child1、Child2,组件App调用了组件Child1和组件Child2,组件Child1和Child2是兄弟组件。

// App.js
import React, { useState } from "react";
import Child1 from "./Child1";
import Child2 from "./Child2";

export default function App() {
  const [child2Value, setChild2Value] = useState({});
  const [deleteItem, setDeleteItem] = useState({});
  const addChild2List = (value) => {
    setChild2Value({ value });
  };
  const deleteChild2List = (index) => {
    setDeleteItem({ index });
  };
  return (
    <div className="App">
      <Child1
        addChild2List={addChild2List}
        deleteChild2List={deleteChild2List}
      />
      <Child2 child2Value={child2Value} deleteItem={deleteItem} />
    </div>
  );
}

// Child1.js
import React, { useState } from "react";

export default function Child1(props) {
  const { addChild2List, deleteChild2List } = props;
  const [inpValue, setInpValue] = useState("");
  const addList = () => {
    if (inpValue) {
      addChild2List(inpValue);
    }
  };
  const deleteList = () => {
    deleteChild2List(0);
  };
  return (
    <>
      <div>
        <h4 style={{ textAlign: "left" }}>我是Child1组件</h4>
        <div style={{ textAlign: "left" }}>
          <input
            placeholder="请输入名字"
            value={inpValue}
            onChange={(e) => setInpValue(e.target.value)}
          />
          <button onClick={addList}>为Child2组件的list添加一项</button>
          <button onClick={deleteList}>删除第一项</button>
        </div>
      </div>
    </>
  );
}


// Child2.js
import React, { useState, useEffect } from "react";

export default function Child2(props) {
  const { child2Value, deleteItem } = props;
  const [list, setList] = useState([]);
  useEffect(() => {
    if (child2Value.value) {
      const { value } = child2Value;
      console.log(value, "value");
      setList((preList) => {
        return [...preList, value];
      });
    }
  }, [child2Value]);
  useEffect(() => {
    setList((preList) => {
      if (preList.length) {
        const newList = preList.filter((_, index) => index !== 0);
        return newList;
      }
      return preList;
    });
  }, [deleteItem]);
  return (
    <ul>
      {list.map((item, index) => (
        <li key={index}>{item}</li>
      ))}
    </ul>
  );
}

    上述代码中,组件Child1中定义了为组件Child2添加和删除列表某一项的方法,再通过App中传递给Child1的方法,将参数传回App组件,再由App通知Child2修改自身list状态,具体请看例子兄弟组件传值

总结

    以上便是对react hooks中组件通信方式的一些整理,上面的这些方法能够处理大部分的业务场景。但是如果遇到需要全局共享的状态,例如登录后的身份信息,或者某个组件需要改变全局的状态,或者需要改变一个行不相干的组件的状态,可能需要借助redux来实现这样的需求。     最后,感谢大家的阅读,如有不足或者错误之处,请各位读者评论指正,谢谢!