Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React 组件通信 #16

Open
peng-yin opened this issue Oct 28, 2019 · 0 comments
Open

React 组件通信 #16

peng-yin opened this issue Oct 28, 2019 · 0 comments

Comments

@peng-yin
Copy link
Owner

父组件内调用子组件方法

<Loading ref={el => {this.el = el}} />

this.el.changeLoading(false); // 子组件某个方法

props 解决数据共享

通过 props 解决数据共享问题,本质上是将数据获取的逻辑放到组件的公共父组件中。代码可能是这样的:

import React, { PureComponent } from 'react';

export default class App extends PureComponent {
 constructor(props) {
    super(props);
    this.state = {
      data: {
        sku: '',
        desc: '',
      },
    };
  }

  componentDidMount() {
    fetch('url', { id: this.props.id })
      .then(resp => resp.json())
      .then(data => this.setState({ data }));
  }

  render() {
    return (
      <div>
        <ProductInfoOne data={this.state.data} />
        <ProductInfoTwo data={this.state.data} />
      </div>
    );
  }
}

function ProductInfoOne({ data }) {
  const { sku } = data;
  return <div>{sku}</div>;
}

function ProductInfoTwo({ data }) {
  const { desc } = data;
  return <div>{desc}</div>;
}

问题是随着嵌套的层次越来越深,数据需要从最外层一直传递到最里层,整个代码的可读性和维护性会变差。我们希望打破数据「层层传递」而子组件也能取到父辈组件中的数据。

Context API

React 16.3 的版本引入了新的 Context API,Context API 本身就是为了解决嵌套层次比较深的场景中数据传递的问题,看起来非常适合解决我们上面提到的问题。我们尝试使用 Context API 来解决我们的问题:

// context.js
const ProductContext = React.createContext({
  sku: '',
  desc: '',
});

export default ProductContext;

// App.js
import React, { PureComponent } from 'react';
import ProductContext from './context';

const Provider = ProductContext.Provider;

export default class App extends PureComponent {
 constructor(props) {
    super(props);
    this.state = {
      data: {
        sku: '',
        desc: '',
      },
    };
  }

  componentDidMount() {
    fetch('url', { id: this.props.id })
      .then(resp => resp.json())
      .then(data => this.setState({ data }));
  }

  render() {
    return (
      <Provider value={this.state.data}>
        <ProductInfoOne />
        <ProductInfoTwo />
      </Provider>
    );
  }
}

// ProductInfoOne.js
import React, { PureComponent } from 'react';
import ProductContext from './context';

export default class ProductInfoOne extends PureComponent {
  static contextType = ProductContext;

  render() {
    const { sku } = this.context;
    return <div>{sku}</div>;
  }
}

// ProductInfoTwo.js
import React, { PureComponent } from 'react';
import ProductContext from './context';

export default class ProductInfoTwo extends PureComponent {
  static contextType = ProductContext;

  render() {
    const { desc } = this.context;
    return <div>{desc}</div>;
  }
}

Redux

// store.js
import { createStore } from 'redux';
import reducer from './reducer';

const store = createStore(reducer);

export default store;

// reducer.js
import * as actions from './actions';
import { combineReducers } from 'redux';

function ProductInfo(state = {}, action) {
  switch (action.type) {
    case actions.SET_SKU: {
      return { ...state, sku: action.sku };
    }
    case actions.SET_DESC: {
      return { ...state, desc: action.desc };
    }
    case actions.SET_DATA: {
      return { ...state, ...action.data };
    }
    default: {
      return state;
    }
  }
}

const reducer = combineReducers({
  ProductInfo,
});

export default reducer;

// action.js
export const SET_SKU = 'SET_SKU';
export const SET_DESC = 'SET_DESC';
export const SET_DATA = 'SET_DATA';

export function setSku(sku) {
  return {
    type: SET_SKU,
    sku,
  };
}

export function setDesc(desc) {
  return {
    type: SET_DESC,
    desc,
  };
}

export function setData(data) {
  return {
    type: SET_DESC,
    data,
  };
}

// App.js
import React, { PureComponent } from 'react';
import { Provider } from 'react-redux';
import store from './store';
import * as actions from './actions';

class App extends PureComponent {
  componentDidMount() {
    fetch('url', { id: this.props.id })
      .then(resp => resp.json())
      .then(data => this.props.dispatch(actions.setData(data)));
  }

  render() {
    return (
      <Provider store={store}>
        <ProductInfoOne />
        <ProductInfoTwo />
      </Provider>
    );
  }
}

function mapStateToProps() {
  return {

  };
}

function mapDispatchToProps(dispatch) {
  return {
    dispatch,
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(App);

// ProductInfoOne.js
import React, { PureComponent } from 'react';
import { connect } from 'react-redux';
import * as actions from './actions';

class ProductInfoOne extends PureComponent {
  onEditSku = (sku) => {
    this.props.dispatch(actions.setSku(sku));
  };

  render() {
    const { sku } = this.props.data;
    return (
      <div>{sku}</div>
    );
  }
}

function mapStateToProps(state) {
  return {
    data: state.ProductInfo,
  };
}

function mapDispatchToProps(dispatch) {
  return {
    dispatch,
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(ProductInfoOne);

// ProductInfoTwo.js
import React, { PureComponent } from 'react';
import { connect } from 'react-redux';
import * as actions from './actions';

class ProductInfoTwo extends PureComponent {
  onEditDesc = (desc) => {
    this.props.dispatch(actions.setDesc(desc));
  };

  render() {
    const { desc } = this.props.data;
    return (
      <div>{desc}</div>
    );
  }
}

function mapStateToProps(state) {
  return {
    data: state.ProductInfo,
  };
}

function mapDispatchToProps(dispatch) {
  return {
    dispatch,
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(ProductInfoTwo);

Mobx

Mobx 我理解的最大的好处是简单、直接,数据发生变化,那么界面就重新渲染,在 React 中使用时,我们甚至不需要关注 React 中的 state,我们看下用 MobX 怎么解决我们上面的问题
mobx

// store.js
import { observable } from 'mobx';

const store = observable({
  sku: '',
  desc: '',
});
export default store;

// App.js
import React, { PureComponent } from 'react';
import store from './store.js';

export default class App extends PureComponent {
  componentDidMount() {
    fetch('url', { id: this.props.id })
      .then(resp => resp.json())
      .then(data => Object.assign(store, data));
  }

  render() {
    return (
      <div>
        <ProductInfoOne />
        <ProductInfoTwo />
      </div>
    );
  }
}

// ProductInfoOne.js
import React, { PureComponent } from 'react';
import { action } from 'mobx';
import { observer } from 'mobx-react';
import store from './store';

@observer
class ProductInfoOne extends PureComponent {
  @action
  onEditSku = (sku) => {
    store.sku = sku;
  };

  render() {
    const { sku } = store;
    return (
      <div>{sku}</div>
    );
  }
}

export default ProductInfoOne;

// ProductInfoTwo.js
import React, { PureComponent } from 'react';
import { action } from 'mobx';
import { observer } from 'mobx-react';
import store from './store';

@observer
class ProductInfoTwo extends PureComponent {
  @action
  onEditDesc = (desc) => {
    store.desc = desc;
  };

  render() {
    const { desc } = store;
    return (
      <div>{desc}</div>
    );
  }
}

export default ProductInfoTwo;

GraphQL

@peng-yin peng-yin added the React label Oct 28, 2019
@peng-yin peng-yin removed the React label Dec 25, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant