React中组件之间的值传递Demo

先附上package.json

{
  "name": "transfer",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.12.0",
    "@testing-library/react": "^11.2.6",
    "@testing-library/user-event": "^12.8.3",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-scripts": "4.0.3",
    "web-vitals": "^1.1.1",
    "react-redux": "^5.0.6",
    "redux": "^3.7.2",
    "redux-thunk": "^2.2.0",
    "react-intl": "3.2.0",
    "prop-types": "latest",
    "axios": "0.21.1"
  },
  "devDependencies": {
    "redux-devtools-extension": "^2.13.2"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

1.第一个demo,使用属性props,通过父组件实现兄弟组件之间的值传递

1.1 在src下创建component目录,用来存放组件

1.2 在component目录下创建transferValue目录,存放第一个值传递demo的组件

1.3 在transferValue目录下创建子组件 ChildSearch.jsx

import React from 'react'
import PropTypes from 'prop-types'

export default class ChildSearch extends React.Component {
    state = {
        keyword: ''
    };

    static propTypes = {
        setKeyword: PropTypes.func.isRequired
    };

    render() {
        return (
            <div style={{width: 200, high: 200, backgroundColor: '#eeabbc', padding: 50}}>
                <h3>搜索用户组件</h3>
                <input type="text" placeholder="请输入搜索关键字" name="keyword"
                       onChange={(e) => this.keywordInputOnChangeHandler(e)}/>
                <input type="button" value="开始搜索" onClick={() => this.searchClickHandler()}/>
            </div>
        )
    }

    /**
     * 输入框内容改变事件
     *
     * @param e 事件
     */
    keywordInputOnChangeHandler = (e) => {
        const inputKeyword = e.target.value;
        this.setState({
            keyword: inputKeyword
        }, () => {
            console.log("1.【搜索组件】输入框搜索内容 keyword = ", this.state.keyword)
        })
    };

    /**
     * 点击事件
     */
    searchClickHandler = () => {
        const {keyword} = this.state;
        console.log(`2.【搜索组件】触发点击事件 keyword.trim()=${keyword.trim()}`);
        if (!keyword.trim()) return;
        console.log("3.【搜索组件】通过props属性方法setKeyword,调用父组件setKeyword方法");
        this.props.setKeyword(keyword)
    }
}

1.4 在transferValue目录下创建子组件  ChildList.jsx

import React from 'react'
import PropTypes from 'prop-types'
import Axios from 'axios'

export default class ChildList extends React.Component {
    static propTypes = {
        keyword: PropTypes.string.isRequired,
    };

    state = {
        userList: [],
    };

    componentWillReceiveProps(newProps) {
        const {keyword} = newProps;
        console.log("5.【列表组件】列表组件获取到搜索组件的keyword,keyword = ", keyword);
        // Axios.get(`http://localhost:3000/search?keyword=${keyword}`)
        //     .then(res => {
        //         if (res.data.code === 0) {
        //             const userList = res.data.data;
        //             console.log(`userList=${JSON.stringify(userList)}`);
        //             this.setState({userList})
        //         }
        //     })
        const ResultUserList = [{id: keyword, name: '刘德华', age: 58}];
        console.log(`6.【列表组件】从ajax返回的结果userList=${JSON.stringify(ResultUserList)}`);
        this.setState({
            userList: ResultUserList,
        }, () => {
            console.log("6.1【列表组件】修改state.userList值,state = ", this.state);
        });
    }

    render() {
        return (
            <div style={{width: 200, high: 200, backgroundColor: '#aaaaff', padding: 50}}>
                {
                    console.log("7.【列表组件】渲染页面列表")
                }
                <p>列表结果组件</p>
                {
                    this.state.userList.map(user => <p key={user.id}>{user.id + "--" + user.name + "--" + user.age}</p>)
                }
            </div>
        )
    }
}

1.5 在transferValue目录下创建父组件 Root.jsx

import React from 'react';
import ChildSearch from './ChildSearch';
import ChildList from './ChildList';

export default class Root extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            keyword: ''
        };
        this.setKeyword = this.setKeyword.bind(this)
    }

    setKeyword(inputKeyword) {
        console.log(`4.【Root组件】的setKeyword被执行,修改state.keyword值, keyword=${inputKeyword}`);
        this.setState({
            keyword: inputKeyword
        }, () => {
            console.log("4.1【Root组件】中的state.keyword值被改变 state = ", this.state)
        })
    }

    render() {
        return (
            <div>
                <ChildSearch setKeyword={this.setKeyword}/>
                <ChildList keyword={this.state.keyword}/>
            </div>
        )
    }
}

1.6 修改index.js文件

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Root from "./component/transferValue/Root";

ReactDOM.render(
     <Root/>,
     document.getElementById('root')
 );

2. 第二个demo,使用属性props,通过父组件实现兄弟组件之间的值传递

2.1 在component目录下创建transferValue2目录,存放第二个值传递demo的组件

2.2 在transferValue2目录下创建子组件 Son1.jsx

import React from 'react';
import PropTypes from 'prop-types'

class Son1 extends React.Component {
    static propTypes = {
        msg: PropTypes.func.isRequired,
    };

    constructor() {
        super()
    }

    click = () => {
        // 在这传给父组件一个值
        console.log("1.【Son1组件】点击Son1按钮传值给父组件");
        const value = '这是Son1传过来的';
        this.props.msg(value)
    };

    render() {
        return (
            <React.Fragment>
                {/* 写一个点击按钮   来调用上边的值*/}
                <div style={{width: 200, high: 200, backgroundColor: '#eeabbc', padding: 50}}>
                    <p>Son1</p>
                    <button onClick={this.click}>Son1中的按钮</button>
                </div>
            </React.Fragment>
        )
    }
}

export default Son1;

2.3 在transferValue2目录下创建子组件 Son2.jsx

import React from 'react';
import PropTypes from 'prop-types'

class Son2 extends React.Component {
    static propTypes = {
        str: PropTypes.string.isRequired,
    };

    constructor() {
        super();
    }

    render() {
        return (
            <React.Fragment>
                {/* 第二个子组件通过props 这个属性来接受父组件传过来的str */}
                <div style={{width: 200, high: 200, backgroundColor: '#aaaaff', padding: 50}}>
                    <p>Son2</p>
                    <p>接收Son1的值: </p>
                    {
                        console.log("3.【Son2组件】从props属性str中获取到父组件的this.state.message值, this.props.str = ", this.props.str)
                    }
                    <p>{this.props.str}</p>
                </div>
            </React.Fragment>
        )
    }
}

export default Son2;

2.4 在transferValue2目录下创建父组件 Father.jsx

import React from 'react';
import Son1 from './Son1';
import Son2 from './Son2'

class Father extends React.Component {
    constructor() {
        super();
        // 先给message一个空值
        this.state = {
            message: ''
        };
    }

    // 声明一个方法用来接收Son1传来的值
    getMessage = (msg) => {
        console.log("2.【父组件】调用Son1组件的msg属性方法接收Son1传来的值 msg = ", msg);
        this.setState({
            message: msg  // 把Son1传来的值给message
        }, () => {
            console.log("2.1 【父组件】把Son1传来的值给state.message, state = ", this.state)
        });
    };

    render() {
        return (
            <React.Fragment>
                {/* 我们在这里引入子组件 拿到上边声明的方法 */}
                <Son1 msg={this.getMessage}/>
                {/* 这里引入第二个子组件 并声明一个属性str  把Son1传来的值传过去 */}
                <Son2 str={this.state.message}/>
            </React.Fragment>
        )
    }
}

export default Father;

2.5 修改index.js 内容

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Father from "./component/transferValue2/Father";

ReactDOM.render(
     <Father/>,
     document.getElementById('root')
);

3 第三个demo,使用 Redux 实现兄弟组件之间的值传递

3.1 在component目录下创建transferValue3-redux目录,存放第三个值传递demo的组件

3.2 在transferValue3-redux目录下创建子组件 AddItemChild.jsx

import React, {Component} from "react";
import {connect} from "react-redux";
import {getChangeValueAction, getSubmitValueAction} from "./store/actions";
import store from "./store/store";

class AddItemChild extends Component {

    constructor() {
        super();
        // TODO 获取数据【Reducer(记事本)处理好的新数据】。。。 借书人 (component组件) 向图书管理员 (store) 借书
        this.state = store.getState(); // store可以通过getState()方法获取图书信息(数据)
        // TODO component组件(借书人) 订阅 store(图书管理员) 里面的数据
        store.subscribe(() => this.changeStore()); // 如果数据改变了,就会执行传入subscribe方法的参数
    }

    render() {
        return (
            <div style={{width: 200, high: 200, backgroundColor: '#eeabbc', padding: 50}}>
                <p style={{color: 'green'}}>添加数据组件</p>
                <input value={this.state.inputVal} onChange={(e) => this.changeInput(e)} placeholder="to do item"/>
                <button onClick={() => this.handleSubmit()}>提交</button>
            </div>
        );
    }

    changeInput(e) {
        console.log("1.【添加数据组件】输入框的值改变, 输入值 = ", e.target.value);
        let action = getChangeValueAction(e.target.value);
        // TODO store(图书管理员) 监听 component组件(借书人) 的动作
        store.dispatch(action);
    }

    handleSubmit() {
        console.log("5.【添加数据组件】点击提交按钮提交数据, inputVal = ", this.state.inputVal);
        let action = getSubmitValueAction(this.state.inputVal);
        // TODO store(图书管理员) 监听 component组件(借书人) 的动作
        store.dispatch(action);
    }

    changeStore() {
        console.log("3.【添加数据组件】将从store(图书管理员)获取到的新数据,重新渲染component组件(借书人)里面的数据,state =", store.getState());
        this.setState(store.getState(), () => {
            console.log("3.1【添加数据组件】component组件(借书人)重新渲染后的 state = ", store.getState());
        }); // TODO 将从 store(图书管理员) 获取到的【新数据】重新渲染 component组件(借书人) 里面的数据

    }

}

// 使用react-redux向外暴露组件
export default connect()(AddItemChild);

3.3 在transferValue3-redux目录下创建子组件 ListItemChild.jsx

import React, {Component} from "react";
import {connect} from "react-redux";
import {getDeleteValueAction} from "./store/actions";
import store from "./store/store";

class ListItemChild extends Component {
    constructor() {
        super();
        // TODO 获取数据【Reducer(记事本)处理好的新数据】。。。 借书人 (component组件) 向图书管理员 (store) 借书
        this.state = store.getState(); // store可以通过getState()方法获取图书信息(数据)
        // TODO component组件(借书人) 订阅 store(图书管理员) 里面的数据
        store.subscribe(() => this.changeStore()); // 如果数据改变了,就会执行传入subscribe方法的参数
    }

    render() {
        return (
            <div style={{width: 200, high: 200, backgroundColor: '#aaaaff', padding: 50}}>
                <p style={{color: 'green'}}>显示列表组件</p>
                {
                    // TODO 渲染到页面(借书人component组件)
                    this.state.list.map((item, index) => {
                        return <div key={index} onClick={this.delItem.bind(this, index)}>{item}</div>
                    })
                }
            </div>
        );
    }

    delItem(index) {
        let action = getDeleteValueAction(index);
        // TODO store(图书管理员) 监听 component组件(借书人) 的动作
        store.dispatch(action);
    }

    changeStore() {
        console.log("4.【列表组件】将从 store(图书管理员) 获取到的【新数据】重新渲染 component组件(借书人) 里面的数据 state = ", store.getState());
        this.setState(store.getState(), () => {
            console.log("4.1【列表组件】component组件(借书人)重新渲染后的 state = ", store.getState());
        }); // TODO 将从 store(图书管理员) 获取到的【新数据】重新渲染 component组件(借书人) 里面的数据
    }
}

// 使用react-redux向外暴露组件
export default connect()(ListItemChild);

3.4 在transferValue3-redux目录下创建父组件 TodoRoot.jsx

import React, {Component} from "react";
import {connect} from "react-redux";
import AddItemChild from "./AddItemChild";
import ListItemChild from "./ListItemChild";

class TodoRoot extends Component {

    constructor(props) {
        super(props);
    }

    render() {
        return (
            <div style={{position: 'absolute', top: '30%', left: '30%'}}>
                <AddItemChild/>
                <ListItemChild/>
            </div>
        )
    }
}

// export default TodoList;

// 使用react-redux向外暴露组件
export default connect()(TodoRoot);

3.5 在transferValue3-redux目录下创建store目录,在store目录下分别创建4个js文件:action.js , actionType.js , reducer.js , store.js

3.6 action.js 文件:

import {INPUT_CHANGE_VALUE, INPUT_SUBMIT_VALUE, INPUT_DELETE_VALUE} from "./actionTypes.js"

/**
 * 统一创建action
 *
 * 之前代码我们把action的创建都放在了组件中,写一些简单的项目确实可以这样做,但是如果是大型项目,
 * 就需要将创建action这个动作放入到一个文件进行统一的管理,这样代码的可维护性就比较高了
 */

export let getChangeValueAction = (value) => {
    return {
        type: INPUT_CHANGE_VALUE,
        value,
    }
};

export let getSubmitValueAction = (value) => {
    return {
        type: INPUT_SUBMIT_VALUE,
        value,
    }
};

export let getDeleteValueAction = (value) => {
    return {
        type: INPUT_DELETE_VALUE,
        value,
    }
};

3.7 actionType.js 文件:

export const INPUT_CHANGE_VALUE="input_change_value";
export const INPUT_SUBMIT_VALUE="input_submit_value";
export const INPUT_DELETE_VALUE="input_delete_value";

3.8 reducer.js 文件:

import {combineReducers} from "redux";
import {INPUT_CHANGE_VALUE, INPUT_SUBMIT_VALUE, INPUT_DELETE_VALUE} from "./actionTypes.js"

let defaultState = {
    inputVal: "",
    list: ["《三国演义》", "《水浒传》", "《西游记》", "《红楼梦》"],
};

/**
 * 记事本 (reducer)
 */
// TODO defaultState 是这个图书馆所有书籍的存放的原始信息,
// TODO action 动作(借书人说我要借书)
export default (state = defaultState, action) => {
    console.log(state, action);

    /*
     Reducer(记事本) 会得到原始store(图书管理员)里面的数据,并得知有哪个数据要改变,还有改变后数据的值,
     知道这些后,Reducer(记事本)会将处理好的新数据返回给store(图书管理员)
     注意:
       reducer(记事本)虽然能获取到store(图书管理员)里面的数据,但是它不能直接将数据进行修改,只能对拷贝后的数据修改,最后返回给store(图书管理员)
     */
    if (action.type === INPUT_CHANGE_VALUE) {
        let newState = JSON.parse(JSON.stringify(state));
        newState.inputVal = action.value;
        console.log("2.【reducer】记事本将处理好的新数据返回给store(图书管理员), newState = ", newState);
        return newState; // TODO Reducer(记事本)会将处理好的 【新数据】 返回给store(图书管理员)
    } else if (action.type === INPUT_SUBMIT_VALUE) {
        let newState = JSON.parse(JSON.stringify(state));
        newState.inputVal = "";
        newState.list.push(action.value);
        console.log("6.【reducer】记事本将处理好的新数据返回给store(图书管理员), newState = ", newState);
        return newState;
    } else if (action.type === INPUT_DELETE_VALUE) {
        let newState = JSON.parse(JSON.stringify(state));
        newState.list.splice(action.value, 1);
        return newState;
    }
    console.log("臥槽");
    return state; // TODO state 是记录本 (reducer) 返回给图书管理员(store) 的图书信息
}

// TODO 这个方式暴露有问题
// export default combineReducers({
//     fun
// })

3.9 store.js 文件:

import {createStore, applyMiddleware} from 'redux'; // 引入createStore来创建store
import thunk from 'redux-thunk' // 应用上异步中间件
import {composeWithDevTools} from 'redux-devtools-extension'
import reducer from './reducer'; // 记事本

/**
 * 图书管理员 (store)
 */
// TODO 图书管理员(Store)并不知道图书馆里都放了哪些书还有书的位置, 他要借助记录本(reducer)来查看图书的信息
const store = createStore(
    reducer,
    composeWithDevTools(applyMiddleware(thunk)) // 应用上异步中间件
);
export default store;

// 参数:window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
// 用来方便调试,首先应该安装react-devtools调试工具,方便以后数据的查看,下载好了直接拖拽到谷歌浏览器的扩展窗口即可使用

3.9 修改index.js内容

import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import './index.css';
import store from './component/transferValue3-redux/store/store';
import Root from "./component/transferValue/Root";
import Father from "./component/transferValue2/Father";
import TodoRoot from "./component/transferValue3-redux/TodoRoot";

// ReactDOM.render(
//     <Root/>,
//     document.getElementById('root')
// );

// ReactDOM.render(
//     <Father/>,
//     document.getElementById('root')
// );

ReactDOM.render(
    (
        <Provider store={store}>
            <TodoRoot/>
        </Provider>
    ),
    document.getElementById('root')
);

最后附上项目目录结构:

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值