Reduxの代わりに新しいReact Context APIを使用して新しいアプリを開発しています。以前は、Redux
を使用して、たとえばユーザーのリストを取得する必要があるときにcomponentDidMount
私のアクションですが、現在Reactコンテキストを使用して、私のアクションはレンダリング関数内のConsumer内に存在します。つまり、レンダリング関数が呼び出されるたびにアクションを呼び出して、ユーザーリストを作成しますが、それはよくありません。なぜなら、私は不必要な要求をたくさんしているからです。
では、レンダリングで呼び出すのではなく、componentDidMount
のように、アクションを1回だけ呼び出すことができますか?
例示するために、次のコードを見てください。
次のように、すべてのProviders
を1つのコンポーネントにラップするとします。
import React from 'react';
import UserProvider from './UserProvider';
import PostProvider from './PostProvider';
export default class Provider extends React.Component {
render(){
return(
<UserProvider>
<PostProvider>
{this.props.children}
</PostProvider>
</UserProvider>
)
}
}
次に、次のように、すべてのアプリをラップするこのProviderコンポーネントを配置します。
import React from 'react';
import Provider from './providers/Provider';
import { Router } from './Router';
export default class App extends React.Component {
render() {
const Component = Router();
return(
<Provider>
<Component />
</Provider>
)
}
}
これで、たとえばユーザービューでは、次のようになります。
import React from 'react';
import UserContext from '../contexts/UserContext';
export default class Users extends React.Component {
render(){
return(
<UserContext.Consumer>
{({getUsers, users}) => {
getUsers();
return(
<h1>Users</h1>
<ul>
{users.map(user) => (
<li>{user.name}</li>
)}
</ul>
)
}}
</UserContext.Consumer>
)
}
}
私が欲しいのはこれです:
import React from 'react';
import UserContext from '../contexts/UserContext';
export default class Users extends React.Component {
componentDidMount(){
this.props.getUsers();
}
render(){
return(
<UserContext.Consumer>
{({users}) => {
getUsers();
return(
<h1>Users</h1>
<ul>
{users.map(user) => (
<li>{user.name}</li>
)}
</ul>
)
}}
</UserContext.Consumer>
)
}
}
ただし、当然、上記の例は、getUsers
が[ユーザー]ビューの小道具に含まれていないため機能しません。これがまったく可能であれば、それを行う正しい方法は何ですか?
EDIT:v16.8.0に反応フックが導入されたため、 useContext
フックを使用して、機能コンポーネントでコンテキストを使用できます
const Users = () => {
const contextValue = useContext(UserContext);
// rest logic here
}
EDIT:バージョン16.6.0以降。 this.context
のようなライフサイクルメソッドでコンテキストを利用できます
class Users extends React.Component {
componentDidMount() {
let value = this.context;
/* perform a side-effect at mount using the value of UserContext */
}
componentDidUpdate() {
let value = this.context;
/* ... */
}
componentWillUnmount() {
let value = this.context;
/* ... */
}
render() {
let value = this.context;
/* render something based on the value of UserContext */
}
}
Users.contextType = UserContext; // This part is important to access context values
バージョン16.6.0より前は、次の方法で実行できました。
ライフサイクルメソッドでコンテキストを使用するには、次のようにコンポーネントを記述します。
class Users extends React.Component {
componentDidMount(){
this.props.getUsers();
}
render(){
const { users } = this.props;
return(
<h1>Users</h1>
<ul>
{users.map(user) => (
<li>{user.name}</li>
)}
</ul>
)
}
}
export default props => ( <UserContext.Consumer>
{({users, getUsers}) => {
return <Users {...props} users={users} getUsers={getUsers} />
}}
</UserContext.Consumer>
)
通常、アプリ内のコンテキストを1つ維持し、上記のログインをHOCにパッケージ化して再利用できるようにします。次のように書くことができます
import UserContext from 'path/to/UserContext';
const withUserContext = Component => {
return props => {
return (
<UserContext.Consumer>
{({users, getUsers}) => {
return <Component {...props} users={users} getUsers={getUsers} />;
}}
</UserContext.Consumer>
);
};
};
そして、あなたはそれを次のように使うことができます
export default withUserContext(User);
さて、私はこれを制限付きで行う方法を見つけました。 with-context
ライブラリを使用して、すべてのコンシューマデータをコンポーネントの小道具に挿入できました。
ただし、同じコンポーネントに複数のコンシューマーを挿入するのは複雑で、このライブラリーを使用して混合コンシューマーを作成する必要があります。これにより、コードがエレガントにならず、非生産的になります。
このライブラリへのリンク: https://github.com/SunHuawei/with-context
編集:実際には、with-context
が提供するマルチコンテキストAPIを使用する必要はありません。実際、シンプルなAPIを使用して、コンテキストごとにデコレーターを作成できます。複数のコンシューマーを使用する場合コンポーネントでは、クラスの上にデコレータを必要なだけ宣言します!
私の側では、.bind(this)
をイベントに追加するだけで十分でした。これが私のコンポーネントの外観です。
// Stores File
class RootStore {
//...States, etc
}
const myRootContext = React.createContext(new RootStore())
export default myRootContext;
// In Component
class MyComp extends Component {
static contextType = myRootContext;
doSomething() {
console.log()
}
render() {
return <button onClick={this.doSomething.bind(this)}></button>
}
}