Изменение стиля «переключаемых» элементов в списке - PullRequest
0 голосов
/ 09 сентября 2018

У меня проблемы с изменением стиля только одного элемента в списке. Ниже мой класс Main, а также класс StationDetails, который является компонентом, который я создал для визуализации элементов списка один за другим.

В StationDetails есть одна строка (строка 31), с которой я не могу понять проблему. Я хочу стилизовать компонент в зависимости от того, включен ли идентификатор элемента в список activeStations.

Вот строка:

 style={activeStations.includes(stations.id) ? pressedStyle : buttonStyle}

Вот мой Main класс

import React, { Component } from "react"
import axios from "axios"
import { Text, View, ScrollView } from "react-native"
import StationDetails from "./StationDetails"

class Main extends Component {
  constructor(props) {
    super(props)
    this.state = { stations: [], pressStatus: false, activeStations: [] }
    this.handleClick = this.handleClick.bind(this)
  }

  componentWillMount() {
    axios
      .get("https://api.citybik.es/v2/networks/trondheim-bysykkel")
      .then(response =>
        this.setState({ stations: response.data.network.stations })
      )
  }

  handleClick() {
    this.setState({ pressStatus: !this.state.pressStatus })
  }

  renderStations() {
    return this.state.stations.map(stations => (
      <StationDetails
        activeStations={this.state.activeStations}
        handleClick={this.handleClick}
        pressStatus={this.state.pressStatus}
        key={stations.id}
        stations={stations}
      >
        {stations.name}
      </StationDetails>
    ))
  }

  render() {
    return (
      <ScrollView style={{ flex: 1, marginTop: 20 }}>
        {this.renderStations()}
      </ScrollView>
    )
  }
}

export default Main

А вот и мой StationDetails компонент.

import React from "react"
import { Text, View } from "react-native"
import Card from "./felles/Card"
import CardSection from "./felles/CardSection"
import Button from "./felles/Button"

const StationDetails = ({
  stations,
  handleClick,
  pressStatus,
  activeStations
}) => {
  const {
    headerTextStyle,
    leftPartStyle,
    rightPartStyle,
    pressedStyle,
    buttonStyle
  } = styles

  return (
    <Card style={{ flex: 1, flexDirection: "row" }}>
      <CardSection style={leftPartStyle}>
        <Text style={headerTextStyle}>
          {stations.name}
        </Text>
        <Text>
          Free bikes: {stations.free_bikes}
        </Text>
      </CardSection>
      <CardSection style={rightPartStyle}>
        <Button
          onPress={() => {
            if (!activeStations.includes(stations.id)) {
              activeStations.push(stations.id)
            } else {
              activeStations.splice(activeStations.indexOf(stations.id), 1)
            }
          }}
          style={
            activeStations.includes(stations.id) ? pressedStyle : buttonStyle
          }
        >
          Abonner
        </Button>
      </CardSection>

    </Card>
  )
}

const styles = {
  textStyle: {
    fontSize: 14
  },
  leftPartStyle: {
    flex: 3,
    flexDirection: "column",
    justifyContent: "space-between"
  },
  rightPartStyle: {
    flex: 1
  },
  pressedStyle: {
    backgroundColor: "green"
  },
  headerTextStyle: {
    fontSize: 18
  },
  thumbnailStyle: {
    height: 50,
    width: 50
  },
  buttonStyle: {
    backgroundColor: "#fff"
  }
}

export default StationDetails

1 Ответ

0 голосов
/ 10 сентября 2018

Вы пытаетесь установить состояние Main.activeStations из StationDetails, что не рекомендуется. Немного о чем стоит помнить

  • Main's activeStations находится в состоянии локального уровня компонента.
  • Вы не должны пытаться изменить это из дочернего компонента.
  • Поскольку вы присваиваете измененное состояние activeStations для Main.activeStations из StationDetails, ReactNative (RN) не находит различий в состоянии в процессе согласования, поэтому не выполняет повторную визуализацию StationDetails.
  • Нам нужно, чтобы RN повторно отобразил StationDetails, чтобы он отображал правильный стиль кнопок и т. Д.
  • Документация по setState

Вот как бы я это сделал

  • Let Main render StationDetails
  • Получить обратный вызов от StationDetails, на которой была выбрана станция
  • Пусть Main позаботится о мутировании своего внутреннего состояния (activeStations)

Делая это таким образом,

  • StationDetails отвечает только за отображение списка станций с учетом реквизита и ничего больше. Это тупой компонент, который выводит список.
  • Main отвечает за обработку своего внутреннего состояния

Вот результат:

Main.js класс

import React, { Component } from 'react';
import axios from 'axios';
import { ScrollView } from 'react-native';
import StationDetails from './StationDetails';

export default class App extends Component {
  constructor(props) {
    super(props);

    this.onSelectStation = this.onSelectStation.bind(this);

    this.state = {
      stations: [],
      pressStatus: false,
      activeStations: []
    };
  }

  componentWillMount() {
    axios.get('https://api.citybik.es/v2/networks/trondheim-bysykkel')
      .then(response => this.setState({ stations: response.data.network.stations }));
  }

  onSelectStation(stationKey) {
    const { activeStations } = this.state;
    const activeStationsEdit = activeStations;

    if (!activeStations.includes(stationKey)) {
      activeStationsEdit.push(stationKey);
    } else {
      activeStationsEdit.splice(activeStations.indexOf(stationKey), 1);
    }

    this.setState({ activeStations: activeStationsEdit });
  }

  renderStations() {
    return this.state.stations.map((stations) =>
      <StationDetails
        activeStations={this.state.activeStations}
        pressStatus={this.state.pressStatus}
        key={stations.id}
        stations={stations}
        stationId={stations.id}
        onSelectStation={this.onSelectStation}
      >
        {stations.name}
      </StationDetails>
    );
  }


  render() {
    return (
      <ScrollView style={{ flex: 1, marginTop: 20 }}>
        {this.renderStations()}
      </ScrollView>
    );
  }
}

StationDetails класс

import React from 'react';
import { Text, View, TouchableOpacity } from 'react-native';

const StationDetails = ({ stations, activeStations, stationId, onSelectStation }) => {
    const { headerTextStyle, leftPartStyle, container, pressedStyle, buttonStyle } = styles;

    return (
        <View style={container}>
            <View style={leftPartStyle}>
                <Text style={headerTextStyle}>
                    {stations.name}
                </Text>
                <Text>
                    Free bikes: {stations.free_bikes}
                </Text>
            </View>
            <TouchableOpacity
                onPress={() => onSelectStation(stationId)}
                style={activeStations.includes(stations.id) ? pressedStyle : buttonStyle}
            />
        </View>
    );
}

const styles = {
    container: {
        flex: 1,
        flexDirection: 'row',
        marginBottom: 10
    },
    leftPartStyle: {
        flex: 3,
        flexDirection: 'column',
        justifyContent: 'space-between'
    },
    pressedStyle: {
        backgroundColor: 'green',
        flex: 1,
    },
    headerTextStyle: {
        fontSize: 18
    },
    buttonStyle: {
        backgroundColor: 'skyblue',
        flex: 1,
    }
};

export default StationDetails;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...