json-eval-rs 0.0.83

High-performance JSON Logic evaluator with schema validation and dependency tracking. Built on blazing-fast Rust engine.
Documentation
import React, { useState, useEffect } from 'react';
import {
  View,
  Text,
  TextInput,
  StyleSheet,
  useColorScheme,
} from 'react-native';
import { useJSONEval } from '@json-eval-rs/react-native';

const schema = {
  type: 'object',
  properties: {
    quantity: { type: 'number' },
    price: { type: 'number' },
    taxRate: { type: 'number' },
    subtotal: {
      type: 'number',
      $evaluation: {
        '*': [{ var: 'quantity' }, { var: 'price' }]
      }
    },
    tax: {
      type: 'number',
      $evaluation: {
        '*': [{ var: 'subtotal' }, { '/': [{ var: 'taxRate' }, 100] }]
      }
    },
    total: {
      type: 'number',
      $evaluation: {
        '+': [{ var: 'subtotal' }, { var: 'tax' }]
      }
    }
  }
};

export default function DependentFieldsScreen() {
  const isDarkMode = useColorScheme() === 'dark';
  const [quantity, setQuantity] = useState('1');
  const [price, setPrice] = useState('10');
  const [taxRate, setTaxRate] = useState('10');
  const [subtotal, setSubtotal] = useState(0);
  const [tax, setTax] = useState(0);
  const [total, setTotal] = useState(0);
  const evalInstance = useJSONEval({ schema });

  useEffect(() => {
    if (!evalInstance) return;

    const updateCalculations = async () => {
      try {
        const data = {
          quantity: Number(quantity) || 0,
          price: Number(price) || 0,
          taxRate: Number(taxRate) || 0,
        };

        const result = await evalInstance.evaluateDependents({
          changedPaths: ['quantity', 'price', 'taxRate'],
          data,
          nested: true,
        });

        setSubtotal(result.subtotal || 0);
        setTax(result.tax || 0);
        setTotal(result.total || 0);
      } catch (error) {
        console.error('Calculation error:', error);
      }
    };

    updateCalculations();
  }, [evalInstance, quantity, price, taxRate]);

  const inputStyle = [
    styles.input,
    { 
      backgroundColor: isDarkMode ? '#2a2a2a' : '#fff',
      color: isDarkMode ? '#fff' : '#000',
      borderColor: isDarkMode ? '#444' : '#ddd',
    },
  ];

  return (
    <View style={styles.container}>
      <Text style={[styles.title, { color: isDarkMode ? '#fff' : '#000' }]}>
        Dependent Fields Example
      </Text>
      <Text style={[styles.description, { color: isDarkMode ? '#aaa' : '#666' }]}>
        This shows automatic calculation of dependent fields. Change values to see real-time updates.
      </Text>

      <View style={styles.row}>
        <View style={styles.column}>
          <Text style={[styles.label, { color: isDarkMode ? '#fff' : '#000' }]}>
            Quantity
          </Text>
          <TextInput
            style={inputStyle}
            value={quantity}
            onChangeText={setQuantity}
            keyboardType="number-pad"
          />
        </View>

        <View style={styles.column}>
          <Text style={[styles.label, { color: isDarkMode ? '#fff' : '#000' }]}>
            Price ($)
          </Text>
          <TextInput
            style={inputStyle}
            value={price}
            onChangeText={setPrice}
            keyboardType="decimal-pad"
          />
        </View>

        <View style={styles.column}>
          <Text style={[styles.label, { color: isDarkMode ? '#fff' : '#000' }]}>
            Tax (%)
          </Text>
          <TextInput
            style={inputStyle}
            value={taxRate}
            onChangeText={setTaxRate}
            keyboardType="decimal-pad"
          />
        </View>
      </View>

      <View style={[styles.divider, { backgroundColor: isDarkMode ? '#444' : '#e5e7eb' }]} />

      <View style={styles.resultRow}>
        <Text style={[styles.resultLabel, { color: isDarkMode ? '#fff' : '#000' }]}>
          Subtotal:
        </Text>
        <Text style={[styles.resultValue, { color: isDarkMode ? '#fff' : '#000' }]}>
          ${subtotal.toFixed(2)}
        </Text>
      </View>

      <View style={styles.resultRow}>
        <Text style={[styles.resultLabel, { color: isDarkMode ? '#fff' : '#000' }]}>
          Tax ({taxRate}%):
        </Text>
        <Text style={[styles.resultValue, { color: isDarkMode ? '#fff' : '#000' }]}>
          ${tax.toFixed(2)}
        </Text>
      </View>

      <View style={[styles.divider, { backgroundColor: isDarkMode ? '#444' : '#e5e7eb' }]} />

      <View style={styles.resultRow}>
        <Text style={[styles.totalLabel, { color: isDarkMode ? '#fff' : '#000' }]}>
          Total:
        </Text>
        <Text style={styles.totalValue}>
          ${total.toFixed(2)}
        </Text>
      </View>

      <View style={[styles.infoBox, { backgroundColor: isDarkMode ? '#2a2a2a' : '#f9fafb' }]}>
        <Text style={[styles.infoTitle, { color: isDarkMode ? '#fff' : '#000' }]}>
          Evaluation Logic:
        </Text>
        <Text style={[styles.infoText, { color: isDarkMode ? '#aaa' : '#666' }]}>
          {`subtotal = quantity × price\ntax = subtotal × taxRate\ntotal = subtotal + tax`}
        </Text>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 8,
  },
  description: {
    fontSize: 14,
    marginBottom: 24,
  },
  row: {
    flexDirection: 'row',
    gap: 12,
    marginBottom: 24,
  },
  column: {
    flex: 1,
  },
  label: {
    fontSize: 14,
    fontWeight: '600',
    marginBottom: 8,
  },
  input: {
    borderWidth: 1,
    borderRadius: 8,
    paddingHorizontal: 12,
    paddingVertical: 10,
    fontSize: 16,
  },
  divider: {
    height: 1,
    marginVertical: 16,
  },
  resultRow: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    marginVertical: 8,
  },
  resultLabel: {
    fontSize: 16,
    fontWeight: '500',
  },
  resultValue: {
    fontSize: 20,
    fontWeight: 'bold',
  },
  totalLabel: {
    fontSize: 20,
    fontWeight: 'bold',
  },
  totalValue: {
    fontSize: 28,
    fontWeight: 'bold',
    color: '#3b82f6',
  },
  infoBox: {
    marginTop: 24,
    padding: 16,
    borderRadius: 8,
  },
  infoTitle: {
    fontSize: 14,
    fontWeight: '600',
    marginBottom: 8,
  },
  infoText: {
    fontSize: 12,
    fontFamily: 'monospace',
    lineHeight: 18,
  },
});