Chapter 11

headless CMSと連携する CRUD 【作例】

knaka Tech-Blog
knaka Tech-Blog
2021.11.25に更新

概要:

React Nativeで、API で headless CMSと連携する CRUDの例になります。

  • mongoDB版となり、前のvercel + mongoDB atlasに接続する構成です

構成

  • react-native-cli
  • react-native: 0.66.3
  • typescript: 4.4.4
  • windows 10
  • node: 14

参考のコード

https://github.com/kuc-arc-f/react_native13
  • headless CMS

https://github.com/kuc-arc-f/headless-1-ts

準備

config.ts : APIエンドポイント、サイトID, API_KEYの設定

API_URL: "http://localhost",
MY_SITE_ID: "1111",
MY_API_KEY: "1111", 

CRUD作成

  • index: src/MainScreen.tsx
MainScreen.tsx

export class MainScreen extends React.Component<IProps, IState> {
  focusListerner = null;
  constructor(props: any) {
    super(props);
    this.state = {
      items: [],
      id: "",
    };
  }
  async componentDidMount(){
    try{
//console.log("#componentDidMount");
      this.getTasks();
      this.focusListerner = this.props.navigation.addListener("focus", async () => {
        await this.getTasks();
      });      
    } catch (e) {
      console.error(e);
    }    
  }
  async getTasks(){
    try{
      const cfg = config.getConfig();
      //console.log("APOLLO_URL=", cfg.APOLLO_URL);
      const site_id = cfg.MY_SITE_ID;
      const url = cfg.API_URL + "/api/get/find?content=tasks&site_id=" +site_id
      const res = await fetch(url);
      const json = await res.json();
//console.log(json);
      this.setState({ items: json });
    } catch (e) {
      console.error(e);
      throw new Error('Error , getTasks');
    }     
  }   
  render() {
    return (
      <View style={styles.container}>
        <Button
          mode="contained"
          style={{ marginBottom: 16 }}
          onPress={() => {
            this.props.navigation.navigate("ComposeScreen", {
              id: this.state.id,
            });
          }}
        >
          Test
        </Button>
        <FlatList
          style={styles.list}
          data={this.state.items}
          keyExtractor={(item) => `${item.id}`}
          renderItem={({ item }) => (
            <List.Item
              title={item.title}
              description={`作成日時: ${item.created_at}`}
              descriptionStyle={{ textAlign: "right" }}
              onPress={() => {
                this.props.navigation.navigate('ShowScreen', {
                  id: item.id
                });
              }}              
            />
          )}
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  list: {
    flex: 1,
  },
});
  • create : src/ComposeScreen.tsx
ComposeScreen.tsx
export class ComposeScreen extends React.Component<IProps, IState>{
  constructor(props: any) {
    super(props)
    this.state = { title: "" };
  }
  async onPressSave(){
    try{
console.log("#onPressSave");
      const cfg = config.getConfig();
      const item = {
        title: this.state.title,
        content: "",
      }
      const api_key = cfg.MY_API_KEY;
      const res = await fetch(cfg.API_URL + '/api/post/create/tasks', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'apikey': api_key},
        body: JSON.stringify(item),
      });
      if (res.status === 200) {
        this.props.navigation.goBack();
      } else {
        throw new Error(await res.text());
      }            
    } catch (e) {
      console.error(e);
      throw new Error('Error , onPressSave');
    }
  }
  inputValueUpdate = (val :any, prop: any) => {
//console.log(prop);
    if(prop === 'title'){
      this.setState({ title: val });
    }
  }
  render(){
    return (
      <KeyboardAvoidingView
        style={styles.container}
      >
        <TextInput
          style={{ marginBottom: 16 }}
          placeholder="文字を入力してください"
          value={this.state.title}
          onChangeText={(val) => this.inputValueUpdate(val, 'title')}
        />
        <Button
          mode="contained"
          onPress={() => this.onPressSave()}
        >
          保存
        </Button>
      </KeyboardAvoidingView>
    )
  }


};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 16,
  },
});

  • show: src/ShowScreen.tsx

  • edit: src/EditScreen.tsx


関連のページ

https://zenn.dev/knaka0209/books/2f0e049833a8c4/viewer/bb75e6

https://zenn.dev/knaka0209/books/2f0e049833a8c4/viewer/61c80c

https://zenn.dev/knaka0209/books/2f0e049833a8c4/viewer/0c0f47

https://zenn.dev/knaka0209/articles/c86e58758d649a

....