Правильный способ обмена функциями между компонентами в React
У меня есть несколько компонентов, которые все должны делать то же самое. (Простая функция, которая отображает их дочерние компоненты и делает что-то для каждого). В настоящий момент я определяю этот метод в каждом из компонентов. Но я только хочу определить его один раз.
Я мог бы определить его в компоненте верхнего уровня, а затем передать его как опору. Но это не совсем правильно. Это скорее функция библиотеки, чем поддержка. (Мне кажется).
Каков правильный способ сделать это?
Ответы
Ответ 1
Если вы используете что-то вроде browserify, вы можете иметь внешний файл i.e util.js, который экспортирует некоторые служебные функции.
var doSomething = function(num) {
return num + 1;
}
exports.doSomething = doSomething;
Затем требуйте его при необходимости
var doSomething = require('./util.js').doSomething;
Ответ 2
Utils.js с последним синтаксисом Javascript ES6
Создайте файл Utils.js
, например, с несколькими функциями и т.д.
const someCommonValues = ['common', 'values'];
export const doSomethingWithInput = (theInput) => {
//Do something with the input
return theInput;
};
export const justAnAlert = () => {
alert('hello');
};
Затем в ваших компонентах, для которых вы хотите использовать функции util, импортируйте нужные функции. Вам не нужно импортировать все
import {doSomethingWithInput, justAnAlert} from './path/to/utils.js/file'
А затем используйте эти функции внутри компонента следующим образом:
justAnAlert();
<p>{doSomethingWithInput('hello')}</p>
Ответ 3
Вот несколько примеров того, как вы можете повторно использовать функцию (FetchUtil.handleError
) в компоненте React (App
).
Решение 1. Использование синтаксиса модуля CommonJS
module.exports = {
handleError: function(response) {
if (!response.ok) throw new Error(response.statusText);
return response;
},
};
Решение 2. Использование createClass (React v16)
Util/FetchUtil.js
const createReactClass = require('create-react-class');
const FetchUtil = createReactClass({
statics: {
handleError: function(response) {
if (!response.ok) throw new Error(response.statusText);
return response;
},
},
render() {
},
});
export default FetchUtil;
Примечание. Если вы используете React v15.4 (или ниже), вам нужно импортировать createClass
следующим образом:
import React from 'react';
const FetchUtil = React.createClass({});
Источник: https://reactjs.org/blog/2017/04/07/react-v15.5.0.html#migrating-from-reactcreateclass
Компонент (который использует FetchUtil)
компоненты/App.jsx
import Categories from './Categories.jsx';
import FetchUtil from '../utils/FetchUtil';
import Grid from 'material-ui/Grid';
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {categories: []};
}
componentWillMount() {
window
.fetch('/rest/service/v1/categories')
.then(FetchUtil.handleError)
.then(response => response.json())
.then(categories => this.setState({...this.state, categories}));
}
render() {
return (
<Grid container={true} spacing={16}>
<Grid item={true} xs={12}>
<Categories categories={this.state.categories} />
</Grid>
</Grid>
);
}
}
export default App;
Ответ 4
Другой надежный вариант, кроме создания файла утилит, заключается в использовании компонента более высокого порядка для создания оболочки withComponentMapper()
. Этот компонент будет принимать компонент в качестве параметра и возвращать его обратно с помощью функции componentMapper()
, переданной в качестве реквизита.
Это считается хорошей практикой в React. Вы можете узнать, как это сделать, подробно здесь.
Ответ 5
Если вы хотите манипулировать состоянием в вспомогательных функциях, выполните следующие действия:
Создайте файл Helpers.js:
export function myFunc(){
return this.state.name; //define it according to your needs
}
Импортируйте вспомогательную функцию в файл компонента:
import {myFunc} from 'path-to/Helpers.js'
В своем конструкторе добавьте эту вспомогательную функцию в класс
constructor(){
this.myFunc = myFunc.bind(this)
}
В вашей функции рендеринга используйте его:
render(){
<div>{this.myFunc()}</div>
}
Ответ 6
Звучит как служебная функция, в этом случае почему бы не поместить ее в отдельный статический служебный модуль?
В противном случае, если вы используете транспилер вроде Babel, вы можете использовать статические методы es7:
class MyComponent extends React.Component {
static someMethod() { ...
Или, если вы используете React.createClass, вы можете использовать объект statics:
var MyComponent = React.createClass({
statics: {
customMethod: function(foo) {
return foo === 'bar';
}
}
Однако я не рекомендую эти параметры, нет смысла включать компонент для метода утилиты.
Кроме того, вы не должны передавать метод через все ваши компоненты в качестве опоры, которые будут тесно связаны с ними и сделать рефакторинг более болезненным. Я советую простой старый служебный модуль.
Другой вариант - использовать mixin для расширения класса, но я не рекомендую это делать, поскольку вы не можете сделать это в es6 + (и я не вижу преимущества в этом случае).
Ответ 7
Это зависит от того, насколько логика компонентов связана друг с другом.
Если логика относительно связана (они используются вместе только в одном приложении), то вы должны делить состояния между компонентами. Но если ваша логика имеет отдаленное отношение (например, математическая утилита, текстовая утилита), то вам следует создавать и импортировать функции класса утилит.
Относительно связанные компоненты могут быть созданы со ссылками обратного вызова, как это, в ./components/App.js
...
<SomeItem
ref={(instance) => {this.childA = instance}}
/>
<SomeOtherItem
ref={(instance) => {this.childB = instance}}
/>
И тогда вы можете использовать общие функции между ними, как это...
this.childA.investigateComponent(this.childB); // call childA function with childB as arg
this.childB.makeNotesOnComponent(this.childA); // call childB function with childA as arg
Компоненты типа Util могут быть созданы следующим образом, в ./utils/time.js
...
export const getTimeDifference = function (start, end) {
// return difference between start and end
}
И тогда их можно использовать вот так, в ./components/App.js
...
import React from 'react';
import {getTimeDifference} from './utils/time.js';
export default class App extends React.Component {
someFunction() {
console.log(getTimeDifference("19:00:00", "20:00:00"));
}
}
Ответ 8
Разве вы не используете для этого Mixin? См. https://facebook.github.io/react/docs/reusable-components.html
Хотя они выходят из моды, см. https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750
Может быть полезно